Question

In this paper, we will search for unusual clusters of complementary palindromes. The overarching research question is: “How do we find clusters of palindromes? How do we determine whether a cluster is just a chance occurrence or a potential replication site? Based on our analysis, we will then provide recommendations to biologists who are about to start experimentally searching for the origin of replication.

Setup

locations <- read.table('hcmv-25kgjn1-1rfrtkc.txt', header=TRUE)$location  # Original
health <- read.csv('RAW_DATA-2iwcznn-2kr2xw0.csv', header=TRUE)  # Additional
N <- 229354  # Base pairs
n <- 296  # Palindromes

Scenario 1: Random Scatter

To begin, pursue the point of view that structure in the data is indicated by departures from a uniform scatter of palindromes across the DNA.

Of course, a random uniform scatter does that mean that palindromes will be equally spaced as milestones on a freeway. There will be some gaps on the DNA where no palindromes occur, and there will be some clumping together of palindromes.

To look for structure examine the locations of the palindromes, the spacing between palindromes, and the counts of palindromes in non overlapping regions of the DNA. One starting place might be to see first how random scatter looks by using a computer to simulate it.

A computer can simulate 296 palindrome sites chosen at random along a DNA sequence of 229,354 bases using a pseudo random number generator. When this is done several times, by making seller sets of simulated palindrome locations, then the real data can be compared to the simulated data.

set.seed(0)
color <- 'red'
# Generate 3 samples from the uniform distribution with the same size and bounds as our data
samples=list(sort(runif(n, min=0, max=N)), sort(runif(n, min=0, max=N)), sort(runif(n, min=0, max=N)))
# Dot plot of locations of palindromes in original data and uniform scatter
title1 <- 'Locations of Palindromes'
title2 <- c(title1,'(Simulated)')
x.axis <- 'Base Pair'
symbol <- 3
stripchart(locations, pch=symbol, col=color, main=title1, xlab=x.axis)

for (sample in samples) {
  stripchart(sample, pch=symbol, main=title2, xlab=x.axis)
}

# Additional dot plot of locations of palindromes in original data and uniform scatter
dotchart(locations, color=color, main=title1, xlab=x.axis)

for (sample in samples) {
  dotchart(sample, main=title2, xlab=x.axis)
}

# Histogram of locations of palindromes in original data and uniform scatter
bins <- 35
hist(locations, col=color, breaks=bins, main=title1, xlab=x.axis)

for (sample in samples) {
  hist(sample, breaks=bins, main=title2, xlab=x.axis)
}

# Scatterplot of spacing between consecutive palindromes
title1 <- 'Spacing between Consecutive Palindromes'
title2 <- c(title1,'(Simulated)')
x.axis <- 'Base Pair Location'
y.axis <- 'Distance (Base Pairs) from Previous Palindrome'
y.range <- c(0,5000)
plot(locations[-1], diff(locations), col=color, ylim=y.range, main=title1, xlab=x.axis, ylab=y.axis)

for (sample in samples) {
  plot(sample[-1], diff(sample), ylim=y.range, main=title2, xlab=x.axis, ylab=y.axis)
}

# Histogram of counts of palindromes in non-overlapping regions in original data and uniform scatter
interval.length <- 2500
title1 <- paste('Number of Palindromes in Non-Overlapping Regions of Length', interval.length)
title2 <- c(title1,'(Simulated)')
x.axis <- 'Number of Palindromes'
bins <- seq(0,20,1)
hist(as.vector(table(cut(locations, breaks=seq(0,N,interval.length), include.lowest=TRUE))), breaks=bins, col=color, main=title1, xlab=x.axis)

for (sample in samples) {
  hist(as.vector(table(cut(sample, breaks=seq(0,N,interval.length), include.lowest=TRUE))), breaks=bins, main=title2, xlab=x.axis)
}

Scenario 2: Locations and Spacings

Use graphical methods to examine the spacings between consecutive palindromes and sum of consecutive pairs, triplets, etc, spacings. Compare what you find to what you would expect to find in a random scatter. Also, use graphical methods to compare locations of the palindromes.

# Chi-square Goodness of Fit Test
# Case 1: k(number of sub-intervals)=20
k <- 20
locations.expected <- n/k
tab <- table(cut(locations, breaks=seq(0, N, length.out=k+1), include.lowest=TRUE))
locations.observed <- as.vector(tab)
chi_2 <- sum((locations.observed - locations.expected)^2/locations.expected)
chi2_compare <- qchisq(p=0.95, df=19)
p_value <- pchisq(chi_2, df=19, lower.tail=FALSE)
print(cat('\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 20 sub-intervals) against uniform distribution\n'))

When conducting chi_square Goodness of fit test comparing locations(divided in 20 sub-intervals) against uniform distribution
NULL
print(paste('The value of chi_square statistic is', chi_2))
[1] "The value of chi_square statistic is 17.9189189189189"
print(paste('The p_value is', p_value))
[1] "The p_value is 0.527860332119311"
## Visualization of the Residual
Residuals <- (locations.observed - locations.expected) / sqrt(locations.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Palindrome locations', main='Plot of Standardized Residual for Locations (divided in 20 sub-intervals)')

# Case 2: k(number of sub-intervals)=30
k <- 30
locations.expected <- n/k
tab <- table(cut(locations, breaks=seq(0, N, length.out=k+1), include.lowest=TRUE))
locations.observed <- as.vector(tab)
chi_2 <- sum((locations.observed - locations.expected)^2/locations.expected)
chi2_compare <- qchisq(p=0.95, df=29)
p_value <- pchisq(chi_2, df=29, lower.tail=FALSE)
print(cat('\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 30 sub-intervals) against uniform distribution\n'))

When conducting chi_square Goodness of fit test comparing locations(divided in 30 sub-intervals) against uniform distribution
NULL
print(paste('The value of chi_square statistic is', chi_2))
[1] "The value of chi_square statistic is 40.6891891891892"
print(paste('The p_value is', p_value))
[1] "The p_value is 0.0732835870345071"
## Visualization of the Residual
Residuals <- (locations.observed - locations.expected) / sqrt(locations.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Palindrome locations', main='Plot of Standardized Residual for Locations (divided in 30 sub-intervals)')

# Case 3: k(number of sub-intervals)=60
k <- 60
locations.expected <- n/k
tab <- table(cut(locations, breaks=seq(0, N, length.out=k+1), include.lowest=TRUE))
locations.observed <- as.vector(tab)
chi_2 <- sum((locations.observed - locations.expected)^2/locations.expected)
chi2_compare <- qchisq(p=0.95, df=59)
p_value <- pchisq(chi_2, df=59, lower.tail=FALSE)
print(cat('\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 60 sub-intervals) against uniform distribution\n'))

When conducting chi_square Goodness of fit test comparing locations(divided in 60 sub-intervals) against uniform distribution
NULL
print(paste('The value of chi_square statistic is', chi_2))
[1] "The value of chi_square statistic is 79"
print(paste('The p_value is', p_value))
[1] "The p_value is 0.0421403871302519"
## Visualization of the Residual
Residuals <- (locations.observed - locations.expected) / sqrt(locations.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Palindrome locations', main='Plot of Standardized Residual for Locations (divided in 60 sub-intervals)')

# Histogram of locations of palindromes in original data and uniform scatter
sample <- runif(n, min=0, max=N)
title <- 'Locations of Palindromes (Original vs. Simulated)'
x.axis <- 'Base Pair'
bins <- 35
hist(locations, breaks=bins, probability=TRUE, col=rgb(1,0,0,0.5), main=title, xlab=x.axis)
lines(density(locations, adjust=2), col=2)
hist(sample, breaks=bins, probability=TRUE, col=rgb(0,0,1,0.5), add=TRUE)
lines(density(sample, adjust=2), col=4)
legend('topright', legend=c('Original', 'Uniform'), lty=c(1,1), col=c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Single Palindrome Spacing
locations.sorted = sort(locations, decreasing = FALSE)
distance.single <- abs(locations.sorted[-1]-locations.sorted[-length(locations.sorted)])
# Histogram of spacings of palindromes in original data and exponential distribution
hist(distance.single, breaks= 15, col = rgb(1,0,0,0.5), probability = TRUE, main = "Consecutive palindrome Spacings Distribution Comparison", xlab = "Distance between Consecutive Palindromes", ylim = c(0,0.001))
lines(density(distance.single, adjust = 2), col = rgb(1,0,0,0.5))
Expo <- rexp(n-1, rate = 1/mean(distance.single))
hist(Expo, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Expo, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 4200, y = 0.0009, legend = c("Sample", "Exponential"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-square Goodness of Fit Test
# Case 1: Divided in 7 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.single, decreasing = FALSE)
lambda <- 1/mean(distance.single)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(0,0.05, 0.1, 0.3, 0.5, 0.7, 0.9,1)))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.single, breaks=spacings.intervals, include.lowest=TRUE)))
contingency_7 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_7
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p=0.95, df=5)
p_value <- pchisq(chi_2, df=5, lower.tail=FALSE)
print(paste('The p_value when the distance is splited into 7 sub-intervals is', p_value))
[1] "The p_value when the distance is splited into 7 sub-intervals is 6.42447875347949e-05"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Palindrome locations', main='Plot of Standardized Residual for Locations (divided in 7 bins)')

# Case 2: Divided in 10 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.single, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.1))))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.single, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_10 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_10
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p=0.95, df=8)
p_value <- pchisq(chi_2, df=8, lower.tail=FALSE)
print(paste('The p_value when the distance is splited into 10 sub-intervals is', p_value))
[1] "The p_value when the distance is splited into 10 sub-intervals is 0.000218981752365422"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Palindrome locations', main='Plot of Standardized Residual for Locations (divided in 10 bins)')

# Case 3: Divided in 20 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.single, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.05))))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.single, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_20 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_20
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p=0.95, df=18)
p_value <- pchisq(chi_2, df=18, lower.tail=FALSE)
print(paste('The p_value when the distance is splited into 20 sub-intervals is', p_value))
[1] "The p_value when the distance is splited into 20 sub-intervals is 0.0117020696134169"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Palindrome locations', main='Plot of Standardized Residual for Locations (divided in 20 bins)')

# Consecutive Pairs
locations.sorted <-  sort(locations, decreasing = FALSE)
locations.pairs <- locations.sorted[-length(locations.sorted)]
distance.pairs <- abs(locations.sorted[-1][-1]-locations.pairs[-length(locations.pairs)])
# Histogram of spacings of palindromes in original data and exponential distribution
hist(distance.pairs, breaks= 15, col = rgb(1,0,0,0.5), probability = TRUE, main = "Consecutive Pairs Spacings Distribution Comparison", xlab = "Distance between Consecutive Pairs of Palindromes Locations", ylim = c(0,0.001))
lines(density(distance.pairs, adjust = 2), col = rgb(1,0,0,0.5))
Expo <- rexp(n-2, rate = 1/mean(distance.pairs))
hist(Expo, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Expo, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 4200, y = 0.0005, legend = c("Sample", "Exponential"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-square Goodness of Fit Test
# Case 1: Divided in 7 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pairs, decreasing = FALSE)
lambda <- 1/mean(distance.pairs)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(0,0.05, 0.1, 0.3, 0.5, 0.7, 0.9,1)))
spacings.expected <- (n-2)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pairs, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_7 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_7
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p=0.95, df=5)
p_value <- pchisq(chi_2, df=5, lower.tail=FALSE)
print(paste('The p_value when the distance is splited into 7 sub-intervals is', p_value))
[1] "The p_value when the distance is splited into 7 sub-intervals is 4.0192806354887e-05"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Spacings between Palindrome Pairs', main='Plot of Standardized Residual for Locations (divided in 7 bins)')

# Case 2: Divided in 10 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pairs, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.1))))
spacings.expected <- (n-2)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pairs, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_10 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_10
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p=0.95, df=8)
p_value <- pchisq(chi_2, df=8, lower.tail=FALSE)
print(paste('The p_value when the distance is splited into 10 sub-intervals is', p_value))
[1] "The p_value when the distance is splited into 10 sub-intervals is 1.68753795541538e-05"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Spacings between Palindrome Pairs', main='Plot of Standardized Residual for Locations (divided in 10 bins)')

# Case 3: Divided in 20 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pairs, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.05))))
spacings.expected <- (n-2)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pairs, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_20 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_20
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p=0.95, df=18)
p_value <- pchisq(chi_2, df=18, lower.tail=FALSE)
print(paste('The p_value when the distance is splited into 20 sub-intervals is', p_value))
[1] "The p_value when the distance is splited into 20 sub-intervals is 0.000159225622786541"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Spacings between Palindrome Pairs", main = "Plot of Standardized Residual for Locations (divided in 20 bins)")

# Consecutive Triplets
locations.sorted <-  sort(locations, decreasing = FALSE)
locations.triplets <- locations.sorted[-length(locations.sorted)]
locations.triplets <- locations.triplets[-length(locations.triplets)]
distance.triplets <- abs(locations.sorted[-1][-1][-1]-locations.triplets[-length(locations.triplets)])
# Histogram of spacings of palindromes in original data and exponential distribution
hist(distance.triplets, breaks= 15, col = rgb(1,0,0,0.5), probability = TRUE, main = "Consecutive Triplets Spacings Distribution Comparison", xlab = "Distance between Consecutive Palindromes Triplets", ylim = c(0,0.0004))
lines(density(distance.triplets, adjust = 2), col = rgb(1,0,0,0.5))
Gam <- rgamma(n-2, 2, rate = 1/mean(distance.pairs))
hist(Gam, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Gam, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 5000, y = 0.0003, legend = c("Sample", "Gamma"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-square Goodness of Fit Test (Need to be changed)
# Case 1: Divided in 7 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.triplets, decreasing = FALSE)
lambda <- 2/mean(distance.pairs)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(0,0.05, 0.1, 0.3, 0.5, 0.7, 0.9,1)))
spacings.expected <- (n-3)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pairs, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_7 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_7
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 5)
p_value <- pchisq(chi_2, df = 5, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 7 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 7 sub-intervals is 9.07421505828639e-47"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 7 bins)")

# Case 2: Divided in 10 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pairs, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.1))))
spacings.expected <- (n-3)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pairs, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_10 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_10
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 8)
p_value <- pchisq(chi_2, df = 8, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 10 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 10 sub-intervals is 8.252676182357e-37"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 10 bins)")

# Case 3: Divided in 20 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pairs, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.05))))
spacings.expected <- (n-3)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pairs, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_20 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_20
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 18)
p_value <- pchisq(chi_2, df = 18, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 20 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 20 sub-intervals is 1.34421275018616e-39"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 20 bins)")

Scenario 3: Counts

Use graphical methods and more formal statistical tests to examine the counts of palindromes in various regions of the DNA. Split the DNA into nonoverlapping regions of equal length to compare the number of palindomres in an interval to the number of that you would expect from uniform random scatter. The counts for shorter regions will be more variable than those for logner regions. Also, consider classifying the regions according to the number of counts.

regionsplit <- function(n.region, gene, site){
  count.int <- table(cut(site, breaks = seq(1, length(gene), length.out=n.region+1), include.lowest=TRUE))
  count.vector <- as.vector(count.int)
  count.tab <- table(count.vector)
  return (count.tab)
}
# Case 1: divided by 40 intervals
n.region <- 40
gene <- seq(1,N)
observed <- as.numeric(regionsplit(n.region, gene, locations))
interval <- as.numeric(names(regionsplit(n.region, gene, locations)))
lambda <- n/n.region
# Histogram of counts of palindromes in original data and poisson distribution
counts <- as.vector(table(cut(locations, breaks = seq(0, N, length.out = n.region+1), include.lowest = TRUE)))
hist(counts, breaks = bins, col = rgb(1,0,0,0.5), probability = TRUE, main = "Counts Distribution Comparison (40 Sub-intervals)", xlab = "Number of Palindromes Sites Inside an Interval", ylim = c(0,0.4))
lines(density(counts, adjust = 2), col = rgb(1,0,0,0.5))
Pois <- rpois(n, lambda)
hist(Pois, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Pois, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 18, y = 0.17, legend = c("Sample", "Poisson"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-sqr Goodness of Fit test
expected <- n.region*exp(-lambda)* lambda**(interval)/factorial(interval)
for (i in c(0:2)){
  expect <- n.region*exp(-lambda)* lambda**(i)/factorial(i)
  expected[1] <- expected[1]+ expect
}
expect <- n.region*exp(-lambda)* lambda**(11)/factorial(11)
expected[8] <- expected[8]+ expect
expected[10] <- 0
for (i in c(1:12)){
  expect <- exp(-lambda)* lambda**(i)/factorial(i)
  expected[10] <- expected[10]+ expect
}
expected[10] <- (1-expected[10])*n.region
counts.expected <- c()
counts.interval <- c()
counts.observed <- c()
# Group bins
counts.expected[1] <- sum(expected[1:2])
counts.expected[2] <- sum(expected[3:4])
counts.expected[3] <- sum(expected[5])
counts.expected[4] <- sum(expected[6:7])
counts.expected[5] <- sum(expected[8:10])
counts.observed[1] <- sum(observed[1:2])
counts.observed[2] <- sum(observed[3:4])
counts.observed[3] <- sum(observed[5])
counts.observed[4] <- sum(observed[6:7])
counts.observed[5] <- sum(observed[8:10])
counts.interval[1] <- interval[2]
counts.interval[2] <- interval[4]
counts.interval[3] <- interval[5]
counts.interval[4] <- interval[7]
counts.interval[5] <- interval[7]+1
counts.table40 <- data.frame(counts.interval,counts.observed,counts.expected)
counts.table40
# Chi-square statistic
chi_2 <- sum((counts.observed - counts.expected)^2/counts.expected)
chi2_compare <- qchisq(p = 0.95, df = 3)
p_value <- pchisq(chi_2, df = 3, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 40 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 40 sub-intervals is 0.365205436984496"
## Visualization of the Residual
Residuals <- (counts.observed - counts.expected) / sqrt(counts.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome counts", main = "Plot of Standardized Residual for Counts (divided in 40 sub-intervals)")

# Case 2: divided by 60 intervals
n.region <- 60
gene <- seq(1,N)
observed <- as.numeric(regionsplit(n.region, gene, locations))
interval <- as.numeric(names(regionsplit(n.region, gene, locations)))
lambda <- n/n.region
# Histogram of counts of palindromes in original data and poisson distribution
counts <- as.vector(table(cut(locations, breaks = seq(0, N, length.out = n.region+1), include.lowest = TRUE)))
hist(counts, breaks = bins, col = rgb(1,0,0,0.5), probability = TRUE, main = "Counts Distribution Comparison (60 Sub-intervals)", xlab = "Number of Palindromes Sites Inside an Interval", ylim = c(0,0.4))
lines(density(counts, adjust = 2), col = rgb(1,0,0,0.5))
Pois <- rpois(n, lambda)
hist(Pois, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Pois, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 18, y = 0.17, legend = c("Sample", "Poisson"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-sqr Goodness of Fit test
expected <- n.region*exp(-lambda)* lambda**(interval)/factorial(interval)
expect <- n.region*exp(-lambda)* lambda**(0)/factorial(0)
expected[1] <- expected[1]+ expect
for (i in c(10:11)){
  expect <- n.region*exp(-lambda)* lambda**(i)/factorial(i)
  expected[9] <- expected[9]+ expect
}
expected[11] <- 0
for (i in c(1:12)){
  expect <- exp(-lambda)* lambda**(i)/factorial(i)
  expected[11] <- expected[11]+ expect
}
expected[11] <- (1-expected[11])*n.region
counts.expected <- c()
counts.interval <- c()
counts.observed <- c()
# Group bins
counts.expected[1] <- sum(expected[1:2])
counts.expected[2:6] <- expected[3:7]
counts.expected[7] <- sum(expected[8:11])
counts.observed[1] <- sum(observed[1:2])
counts.observed[2:6] <- observed[3:7]
counts.observed[7] <- sum(observed[8:11])
counts.interval[1] <- interval[2]
counts.interval[2:6] <- interval[3:7]
counts.interval[7] <- interval[7]+1
counts.table60 <- data.frame(counts.interval,counts.observed,counts.expected)
counts.table60
# Chi-square statistic
chi_2 <- sum((counts.observed - counts.expected)^2/counts.expected)
chi2_compare <- qchisq(p = 0.95, df = 5)
p_value <- pchisq(chi_2, df = 5, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 60 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 60 sub-intervals is 0.406748395085584"
## Visualization of the Residual
Residuals <- (counts.observed - counts.expected) / sqrt(counts.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome counts", main = "Plot of Standardized Residual for Counts (divided in 60 sub-intervals)")

# Case 3: divided by 80 intervals
n.region <- 80
gene <- seq(1,N)
observed <- as.numeric(regionsplit(n.region, gene, locations))
interval <- as.numeric(names(regionsplit(n.region, gene, locations)))
lambda <- n/n.region
# Histogram of counts of palindromes in original data and poisson distribution
counts <- as.vector(table(cut(locations, breaks = seq(0, N, length.out = n.region+1), include.lowest = TRUE)))
hist(counts, breaks = bins, col = rgb(1,0,0,0.5), probability = TRUE, main = "Counts Distribution Comparison (80 Sub-intervals)", xlab = "Number of Palindromes Sites Inside an Interval", ylim = c(0,0.5))
lines(density(counts, adjust = 2), col = rgb(1,0,0,0.5))
Pois <- rpois(n, lambda)
hist(Pois, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Pois, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 18, y = 0.17, legend = c("Sample", "Poisson"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-sqr Goodness of Fit test
expected <- n.region*exp(-lambda)* lambda**(interval)/factorial(interval)
for (i in c(8:9)){
  expect <- n.region*exp(-lambda)* lambda**(i)/factorial(i)
  expected[9] <- expected[9]+ expect
}
expected[10] <- 0
for (i in c(1:10)){
  expect <- exp(-lambda)* lambda**(i)/factorial(i)
  expected[10] <- expected[10]+ expect
}
expected[10] <- (1-expected[10])*n.region
counts.expected <- c()
counts.interval <- c()
counts.observed <- c()
# Group bins
counts.expected[1] <- sum(expected[1:2])
counts.expected[2:6] <- expected[3:7]
counts.expected[7] <- sum(expected[8:10])
counts.observed[1] <- sum(observed[1:2])
counts.observed[2:6] <- observed[3:7]
counts.observed[7] <- sum(observed[8:10])
counts.interval[1] <- interval[2]
counts.interval[2:6] <- interval[3:7]
counts.interval[7] <- interval[7]+1
counts.table60 <- data.frame(counts.interval,counts.observed,counts.expected)
counts.table60
# Chi-square statistic
chi_2 <- sum((counts.observed - counts.expected)^2/counts.expected)
chi2_compare <- qchisq(p = 0.95, df = 5)
p_value <- pchisq(chi_2, df = 5, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 60 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 60 sub-intervals is 0.868215562053925"
## Visualization of the Residual
Residuals <- (counts.observed - counts.expected) / sqrt(counts.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome counts", main = "Plot of Standardized Residual for Counts (divided in 80 sub-intervals)")

Scenario 4: The Biggest Cluster

Does the interval with the greatest number of palindromes indicate a potential origin of replication? Be careful in making your intervals, for any small, but significant deviations from random scatter, such as a tight cluster of a few palindromes, could easily go undetected if the regions examined are too large. Also, if the regions are too small, a cluster of palindromes may be split between adjacent interavls and not appear as a high-count interval.

final <- array(dim=c(500,1))
interval_length <- array(dim=c(500,1))
lamda <- array(dim=c(500,1))
for (k in 20:100){
  tab <- table(cut(locations, breaks=seq(0, N, length.out=k+1), include.lowest=TRUE))
  head(tab,10)
  tab<-as.vector(tab)
  lamda[k,] <-sum(tab)/k
  threshold <-max(tab)
  result <- 0
  interval_length[k,] <- N/k
  for (i in 0:(threshold-1)){
    result <- result+((lamda[k]^i)*exp(-lamda[k])/factorial(i))
  }
  final[k,] <- 1-result^k
}
result <- data.frame(lamda,interval_length,final)
# Display Table containing the probability of a Poisson Distribution having e greatest number of hits at least k for each sub-interval divisions
result[c(40,60,80),]

Additional Scenario: HIV and Age

TODO Description

# Clean out 'unknown' data and convert factor to numerical values
health <- transform(health, age_yrs=as.numeric(age_yrs),
                            hiv=as.character(hiv))
health.ind <- which(health$hiv != 'unknown')
health <- health[health.ind,]
# Total number of people that have hiv
population=nrow(health)
pop_hiv <- nrow(health[which(health$hiv=='positive'),])
# Split the age into four groups
# 0-20
age_first <- health$age_yrs[which(health$age_yrs<21)]
age_proportion_first <- length(age_first)/population
hiv_proportion_first<- nrow(health[which((health$hiv== 'positive') & (health$age_yrs <21)),])/pop_hiv
# 21-40
age_second<-health$age_yrs[which(health$age_yrs>20 & health['age_yrs']<41)]
age_proportion_second <- length(age_second)/population
hiv_proportion_second<-nrow(health[which(health$age_yrs>20 &health$age_yrs<41 & health$hiv=='positive'),])/pop_hiv
# 41-60
age_third<-health$age_yrs[which(health['age_yrs']>40 & health['age_yrs']<61)]
age_proportion_third <- length(age_third)/population
hiv_proportion_third<-nrow(health[which(health$age_yrs>40 & health$age_yrs<61 &health$hiv=='positive'),])/pop_hiv
# 61+
age_last<-health$age_yrs[which(health['age_yrs']>60)]
age_proportion_last <- length(age_last)/population
hiv_proportion_last<-nrow(health[which(health$age_yrs>60 & health$hiv=='positive'),])/pop_hiv
# Expected Data
population_dist <-c(age_proportion_first,age_proportion_second,age_proportion_third,age_proportion_last)
# Observed Data
hiv_dist<-c(hiv_proportion_first,hiv_proportion_second,hiv_proportion_third,hiv_proportion_last)
age_dist <- c("0-20", "21-40", "41-60", "61+")
data.frame(age_dist,population_dist,hiv_dist)
# Goodness-fittest
chi_2 <- sum((hiv_dist - population_dist)^2/population_dist)
chi2_compare <- qchisq(p=0.95, df=3)
p_value <- pchisq(chi_2, df=3, lower.tail=FALSE)
print(paste('The p_value of Goodness of Fit Test is',p_value))
[1] "The p_value of Goodness of Fit Test is 0.919175049520472"
#Visualization
Residuals <- (hiv_dist - population_dist) / sqrt(population_dist)
plot(Residuals, type='h', ylab='Standardized Residuals', xlab='Proportion of Positive HIV', main='Plot of Standardized Residual for Age and HIV Positive')

Null Hypothesis: The proportion of age in the population is unrelated with the proportion of people having hiv.(Age is not an influencing factor for HIV testing positive) Since p-value of this chi-square goodness of fit test is close to 1, we see that deviations as large as ours (or larger) are very likely. In addition, having values of the standardized residual less than 3 suggests that it is a good fit of the age distribution to estimate the people testing positive on hiv. Hence, we reject the null hypothesis and conclude that the distribution of proportion of age matches with the the distribution of people testing positive on HIV.

LS0tCnRpdGxlOiAnQ0FTRSBTVFVEWSAzOiBTRUFSQ0ggRk9SIFRIRSBVTlVTVUFMIENMVVNURVIgSU4gVEhFIFBBTElORFJPTUVTJwpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKIyMgUXVlc3Rpb24KSW4gdGhpcyBwYXBlciwgd2Ugd2lsbCBzZWFyY2ggZm9yIHVudXN1YWwgY2x1c3RlcnMgb2YgY29tcGxlbWVudGFyeSBwYWxpbmRyb21lcy4gVGhlIG92ZXJhcmNoaW5nIHJlc2VhcmNoIHF1ZXN0aW9uIGlzOiDigJxIb3cgZG8gd2UgZmluZCBjbHVzdGVycyBvZiBwYWxpbmRyb21lcz8gSG93IGRvIHdlIGRldGVybWluZSB3aGV0aGVyIGEgY2x1c3RlciBpcyBqdXN0IGEgY2hhbmNlIG9jY3VycmVuY2Ugb3IgYSBwb3RlbnRpYWwgcmVwbGljYXRpb24gc2l0ZT8gQmFzZWQgb24gb3VyIGFuYWx5c2lzLCB3ZSB3aWxsIHRoZW4gcHJvdmlkZSByZWNvbW1lbmRhdGlvbnMgdG8gYmlvbG9naXN0cyB3aG8gYXJlIGFib3V0IHRvIHN0YXJ0IGV4cGVyaW1lbnRhbGx5IHNlYXJjaGluZyBmb3IgdGhlIG9yaWdpbiBvZiByZXBsaWNhdGlvbi4KCgojIyBTZXR1cApgYGB7cn0KbG9jYXRpb25zIDwtIHJlYWQudGFibGUoJ2hjbXYtMjVrZ2puMS0xcmZydGtjLnR4dCcsIGhlYWRlcj1UUlVFKSRsb2NhdGlvbiAgIyBPcmlnaW5hbApoZWFsdGggPC0gcmVhZC5jc3YoJ1JBV19EQVRBLTJpd2N6bm4tMmtyMnh3MC5jc3YnLCBoZWFkZXI9VFJVRSkgICMgQWRkaXRpb25hbAoKTiA8LSAyMjkzNTQgICMgQmFzZSBwYWlycwpuIDwtIDI5NiAgIyBQYWxpbmRyb21lcwpgYGAKCgojIyBTY2VuYXJpbyAxOiBSYW5kb20gU2NhdHRlcgpUbyBiZWdpbiwgcHVyc3VlIHRoZSBwb2ludCBvZiB2aWV3IHRoYXQgc3RydWN0dXJlIGluIHRoZSBkYXRhIGlzIGluZGljYXRlZCBieSBkZXBhcnR1cmVzIGZyb20gYSB1bmlmb3JtIHNjYXR0ZXIgb2YgcGFsaW5kcm9tZXMgYWNyb3NzIHRoZSBETkEuCgoqT2YgY291cnNlLCBhIHJhbmRvbSB1bmlmb3JtIHNjYXR0ZXIgZG9lcyB0aGF0IG1lYW4gdGhhdCBwYWxpbmRyb21lcyB3aWxsIGJlIGVxdWFsbHkgc3BhY2VkIGFzIG1pbGVzdG9uZXMgb24gYSBmcmVld2F5LiBUaGVyZSB3aWxsIGJlIHNvbWUgZ2FwcyBvbiB0aGUgRE5BIHdoZXJlIG5vIHBhbGluZHJvbWVzIG9jY3VyLCBhbmQgdGhlcmUgd2lsbCBiZSBzb21lIGNsdW1waW5nIHRvZ2V0aGVyIG9mIHBhbGluZHJvbWVzLioKClRvIGxvb2sgZm9yIHN0cnVjdHVyZSBleGFtaW5lIHRoZSBsb2NhdGlvbnMgb2YgdGhlIHBhbGluZHJvbWVzLCB0aGUgc3BhY2luZyBiZXR3ZWVuIHBhbGluZHJvbWVzLCBhbmQgdGhlIGNvdW50cyBvZiBwYWxpbmRyb21lcyBpbiBub24gb3ZlcmxhcHBpbmcgcmVnaW9ucyBvZiB0aGUgRE5BLiBPbmUgc3RhcnRpbmcgcGxhY2UgbWlnaHQgYmUgdG8gc2VlIGZpcnN0IGhvdyByYW5kb20gc2NhdHRlciBsb29rcyBieSB1c2luZyBhIGNvbXB1dGVyIHRvIHNpbXVsYXRlIGl0LgoKKkEgY29tcHV0ZXIgY2FuIHNpbXVsYXRlIDI5NiBwYWxpbmRyb21lIHNpdGVzIGNob3NlbiBhdCByYW5kb20gYWxvbmcgYSBETkEgc2VxdWVuY2Ugb2YgMjI5LDM1NCBiYXNlcyB1c2luZyBhIHBzZXVkbyByYW5kb20gbnVtYmVyIGdlbmVyYXRvci4gV2hlbiB0aGlzIGlzIGRvbmUgc2V2ZXJhbCB0aW1lcywgYnkgbWFraW5nIHNlbGxlciBzZXRzIG9mIHNpbXVsYXRlZCBwYWxpbmRyb21lIGxvY2F0aW9ucywgdGhlbiB0aGUgcmVhbCBkYXRhIGNhbiBiZSBjb21wYXJlZCB0byB0aGUgc2ltdWxhdGVkIGRhdGEuKgpgYGB7cn0Kc2V0LnNlZWQoMCkKY29sb3IgPC0gJ3JlZCcKCiMgR2VuZXJhdGUgMyBzYW1wbGVzIGZyb20gdGhlIHVuaWZvcm0gZGlzdHJpYnV0aW9uIHdpdGggdGhlIHNhbWUgc2l6ZSBhbmQgYm91bmRzIGFzIG91ciBkYXRhCnNhbXBsZXM9bGlzdChzb3J0KHJ1bmlmKG4sIG1pbj0wLCBtYXg9TikpLCBzb3J0KHJ1bmlmKG4sIG1pbj0wLCBtYXg9TikpLCBzb3J0KHJ1bmlmKG4sIG1pbj0wLCBtYXg9TikpKQoKIyBEb3QgcGxvdCBvZiBsb2NhdGlvbnMgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgdW5pZm9ybSBzY2F0dGVyCnRpdGxlMSA8LSAnTG9jYXRpb25zIG9mIFBhbGluZHJvbWVzJwp0aXRsZTIgPC0gYyh0aXRsZTEsJyhTaW11bGF0ZWQpJykKeC5heGlzIDwtICdCYXNlIFBhaXInCnN5bWJvbCA8LSAzCnN0cmlwY2hhcnQobG9jYXRpb25zLCBwY2g9c3ltYm9sLCBjb2w9Y29sb3IsIG1haW49dGl0bGUxLCB4bGFiPXguYXhpcykKZm9yIChzYW1wbGUgaW4gc2FtcGxlcykgewogIHN0cmlwY2hhcnQoc2FtcGxlLCBwY2g9c3ltYm9sLCBtYWluPXRpdGxlMiwgeGxhYj14LmF4aXMpCn0KCiMgQWRkaXRpb25hbCBkb3QgcGxvdCBvZiBsb2NhdGlvbnMgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgdW5pZm9ybSBzY2F0dGVyCmRvdGNoYXJ0KGxvY2F0aW9ucywgY29sb3I9Y29sb3IsIG1haW49dGl0bGUxLCB4bGFiPXguYXhpcykKZm9yIChzYW1wbGUgaW4gc2FtcGxlcykgewogIGRvdGNoYXJ0KHNhbXBsZSwgbWFpbj10aXRsZTIsIHhsYWI9eC5heGlzKQp9CgojIEhpc3RvZ3JhbSBvZiBsb2NhdGlvbnMgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgdW5pZm9ybSBzY2F0dGVyCmJpbnMgPC0gMzUKaGlzdChsb2NhdGlvbnMsIGNvbD1jb2xvciwgYnJlYWtzPWJpbnMsIG1haW49dGl0bGUxLCB4bGFiPXguYXhpcykKZm9yIChzYW1wbGUgaW4gc2FtcGxlcykgewogIGhpc3Qoc2FtcGxlLCBicmVha3M9YmlucywgbWFpbj10aXRsZTIsIHhsYWI9eC5heGlzKQp9CgoKCiMgU2NhdHRlcnBsb3Qgb2Ygc3BhY2luZyBiZXR3ZWVuIGNvbnNlY3V0aXZlIHBhbGluZHJvbWVzCnRpdGxlMSA8LSAnU3BhY2luZyBiZXR3ZWVuIENvbnNlY3V0aXZlIFBhbGluZHJvbWVzJwp0aXRsZTIgPC0gYyh0aXRsZTEsJyhTaW11bGF0ZWQpJykKeC5heGlzIDwtICdCYXNlIFBhaXIgTG9jYXRpb24nCnkuYXhpcyA8LSAnRGlzdGFuY2UgKEJhc2UgUGFpcnMpIGZyb20gUHJldmlvdXMgUGFsaW5kcm9tZScKeS5yYW5nZSA8LSBjKDAsNTAwMCkKcGxvdChsb2NhdGlvbnNbLTFdLCBkaWZmKGxvY2F0aW9ucyksIGNvbD1jb2xvciwgeWxpbT15LnJhbmdlLCBtYWluPXRpdGxlMSwgeGxhYj14LmF4aXMsIHlsYWI9eS5heGlzKQpmb3IgKHNhbXBsZSBpbiBzYW1wbGVzKSB7CiAgcGxvdChzYW1wbGVbLTFdLCBkaWZmKHNhbXBsZSksIHlsaW09eS5yYW5nZSwgbWFpbj10aXRsZTIsIHhsYWI9eC5heGlzLCB5bGFiPXkuYXhpcykKfQoKCgojIEhpc3RvZ3JhbSBvZiBjb3VudHMgb2YgcGFsaW5kcm9tZXMgaW4gbm9uLW92ZXJsYXBwaW5nIHJlZ2lvbnMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgdW5pZm9ybSBzY2F0dGVyCmludGVydmFsLmxlbmd0aCA8LSAyNTAwCnRpdGxlMSA8LSBwYXN0ZSgnTnVtYmVyIG9mIFBhbGluZHJvbWVzIGluIE5vbi1PdmVybGFwcGluZyBSZWdpb25zIG9mIExlbmd0aCcsIGludGVydmFsLmxlbmd0aCkKdGl0bGUyIDwtIGModGl0bGUxLCcoU2ltdWxhdGVkKScpCnguYXhpcyA8LSAnTnVtYmVyIG9mIFBhbGluZHJvbWVzJwpiaW5zIDwtIHNlcSgwLDIwLDEpCmhpc3QoYXMudmVjdG9yKHRhYmxlKGN1dChsb2NhdGlvbnMsIGJyZWFrcz1zZXEoMCxOLGludGVydmFsLmxlbmd0aCksIGluY2x1ZGUubG93ZXN0PVRSVUUpKSksIGJyZWFrcz1iaW5zLCBjb2w9Y29sb3IsIG1haW49dGl0bGUxLCB4bGFiPXguYXhpcykKZm9yIChzYW1wbGUgaW4gc2FtcGxlcykgewogIGhpc3QoYXMudmVjdG9yKHRhYmxlKGN1dChzYW1wbGUsIGJyZWFrcz1zZXEoMCxOLGludGVydmFsLmxlbmd0aCksIGluY2x1ZGUubG93ZXN0PVRSVUUpKSksIGJyZWFrcz1iaW5zLCBtYWluPXRpdGxlMiwgeGxhYj14LmF4aXMpCn0KYGBgCgoKIyMgU2NlbmFyaW8gMjogTG9jYXRpb25zIGFuZCBTcGFjaW5ncwpVc2UgZ3JhcGhpY2FsIG1ldGhvZHMgdG8gZXhhbWluZSB0aGUgc3BhY2luZ3MgYmV0d2VlbiBjb25zZWN1dGl2ZSBwYWxpbmRyb21lcyBhbmQgc3VtIG9mIGNvbnNlY3V0aXZlIHBhaXJzLCB0cmlwbGV0cywgZXRjLCBzcGFjaW5ncy4gQ29tcGFyZSB3aGF0IHlvdSBmaW5kIHRvIHdoYXQgeW91IHdvdWxkIGV4cGVjdCB0byBmaW5kIGluIGEgcmFuZG9tIHNjYXR0ZXIuIEFsc28sIHVzZSBncmFwaGljYWwgbWV0aG9kcyB0byBjb21wYXJlIGxvY2F0aW9ucyBvZiB0aGUgcGFsaW5kcm9tZXMuCmBgYHtyfQojIENoaS1zcXVhcmUgR29vZG5lc3Mgb2YgRml0IFRlc3QKIyBDYXNlIDE6IGsobnVtYmVyIG9mIHN1Yi1pbnRlcnZhbHMpPTIwCmsgPC0gMjAKbG9jYXRpb25zLmV4cGVjdGVkIDwtIG4vawp0YWIgPC0gdGFibGUoY3V0KGxvY2F0aW9ucywgYnJlYWtzPXNlcSgwLCBOLCBsZW5ndGgub3V0PWsrMSksIGluY2x1ZGUubG93ZXN0PVRSVUUpKQpsb2NhdGlvbnMub2JzZXJ2ZWQgPC0gYXMudmVjdG9yKHRhYikKY2hpXzIgPC0gc3VtKChsb2NhdGlvbnMub2JzZXJ2ZWQgLSBsb2NhdGlvbnMuZXhwZWN0ZWQpXjIvbG9jYXRpb25zLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHA9MC45NSwgZGY9MTkpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZj0xOSwgbG93ZXIudGFpbD1GQUxTRSkKcHJpbnQoY2F0KCdcbldoZW4gY29uZHVjdGluZyBjaGlfc3F1YXJlIEdvb2RuZXNzIG9mIGZpdCB0ZXN0IGNvbXBhcmluZyBsb2NhdGlvbnMoZGl2aWRlZCBpbiAyMCBzdWItaW50ZXJ2YWxzKSBhZ2FpbnN0IHVuaWZvcm0gZGlzdHJpYnV0aW9uXG4nKSkKcHJpbnQocGFzdGUoJ1RoZSB2YWx1ZSBvZiBjaGlfc3F1YXJlIHN0YXRpc3RpYyBpcycsIGNoaV8yKSkKcHJpbnQocGFzdGUoJ1RoZSBwX3ZhbHVlIGlzJywgcF92YWx1ZSkpCgojIyBWaXN1YWxpemF0aW9uIG9mIHRoZSBSZXNpZHVhbApSZXNpZHVhbHMgPC0gKGxvY2F0aW9ucy5vYnNlcnZlZCAtIGxvY2F0aW9ucy5leHBlY3RlZCkgLyBzcXJ0KGxvY2F0aW9ucy5leHBlY3RlZCkKcGxvdChSZXNpZHVhbHMsIHR5cGU9J2gnLCB5bGFiPSdTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzJywgeGxhYj0nUGFsaW5kcm9tZSBsb2NhdGlvbnMnLCBtYWluPSdQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgTG9jYXRpb25zIChkaXZpZGVkIGluIDIwIHN1Yi1pbnRlcnZhbHMpJykKCiMgQ2FzZSAyOiBrKG51bWJlciBvZiBzdWItaW50ZXJ2YWxzKT0zMAprIDwtIDMwCmxvY2F0aW9ucy5leHBlY3RlZCA8LSBuL2sKdGFiIDwtIHRhYmxlKGN1dChsb2NhdGlvbnMsIGJyZWFrcz1zZXEoMCwgTiwgbGVuZ3RoLm91dD1rKzEpLCBpbmNsdWRlLmxvd2VzdD1UUlVFKSkKbG9jYXRpb25zLm9ic2VydmVkIDwtIGFzLnZlY3Rvcih0YWIpCmNoaV8yIDwtIHN1bSgobG9jYXRpb25zLm9ic2VydmVkIC0gbG9jYXRpb25zLmV4cGVjdGVkKV4yL2xvY2F0aW9ucy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwPTAuOTUsIGRmPTI5KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGY9MjksIGxvd2VyLnRhaWw9RkFMU0UpCnByaW50KGNhdCgnXG5XaGVuIGNvbmR1Y3RpbmcgY2hpX3NxdWFyZSBHb29kbmVzcyBvZiBmaXQgdGVzdCBjb21wYXJpbmcgbG9jYXRpb25zKGRpdmlkZWQgaW4gMzAgc3ViLWludGVydmFscykgYWdhaW5zdCB1bmlmb3JtIGRpc3RyaWJ1dGlvblxuJykpCnByaW50KHBhc3RlKCdUaGUgdmFsdWUgb2YgY2hpX3NxdWFyZSBzdGF0aXN0aWMgaXMnLCBjaGlfMikpCnByaW50KHBhc3RlKCdUaGUgcF92YWx1ZSBpcycsIHBfdmFsdWUpKQoKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChsb2NhdGlvbnMub2JzZXJ2ZWQgLSBsb2NhdGlvbnMuZXhwZWN0ZWQpIC8gc3FydChsb2NhdGlvbnMuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlPSdoJywgeWxhYj0nU3RhbmRhcmRpemVkIFJlc2lkdWFscycsIHhsYWI9J1BhbGluZHJvbWUgbG9jYXRpb25zJywgbWFpbj0nUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIExvY2F0aW9ucyAoZGl2aWRlZCBpbiAzMCBzdWItaW50ZXJ2YWxzKScpCgojIENhc2UgMzogayhudW1iZXIgb2Ygc3ViLWludGVydmFscyk9NjAKayA8LSA2MApsb2NhdGlvbnMuZXhwZWN0ZWQgPC0gbi9rCnRhYiA8LSB0YWJsZShjdXQobG9jYXRpb25zLCBicmVha3M9c2VxKDAsIE4sIGxlbmd0aC5vdXQ9aysxKSwgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpCmxvY2F0aW9ucy5vYnNlcnZlZCA8LSBhcy52ZWN0b3IodGFiKQpjaGlfMiA8LSBzdW0oKGxvY2F0aW9ucy5vYnNlcnZlZCAtIGxvY2F0aW9ucy5leHBlY3RlZCleMi9sb2NhdGlvbnMuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocD0wLjk1LCBkZj01OSkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmPTU5LCBsb3dlci50YWlsPUZBTFNFKQpwcmludChjYXQoJ1xuV2hlbiBjb25kdWN0aW5nIGNoaV9zcXVhcmUgR29vZG5lc3Mgb2YgZml0IHRlc3QgY29tcGFyaW5nIGxvY2F0aW9ucyhkaXZpZGVkIGluIDYwIHN1Yi1pbnRlcnZhbHMpIGFnYWluc3QgdW5pZm9ybSBkaXN0cmlidXRpb25cbicpKQpwcmludChwYXN0ZSgnVGhlIHZhbHVlIG9mIGNoaV9zcXVhcmUgc3RhdGlzdGljIGlzJywgY2hpXzIpKQpwcmludChwYXN0ZSgnVGhlIHBfdmFsdWUgaXMnLCBwX3ZhbHVlKSkKCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAobG9jYXRpb25zLm9ic2VydmVkIC0gbG9jYXRpb25zLmV4cGVjdGVkKSAvIHNxcnQobG9jYXRpb25zLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZT0naCcsIHlsYWI9J1N0YW5kYXJkaXplZCBSZXNpZHVhbHMnLCB4bGFiPSdQYWxpbmRyb21lIGxvY2F0aW9ucycsIG1haW49J1Bsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gNjAgc3ViLWludGVydmFscyknKQoKIyBIaXN0b2dyYW0gb2YgbG9jYXRpb25zIG9mIHBhbGluZHJvbWVzIGluIG9yaWdpbmFsIGRhdGEgYW5kIHVuaWZvcm0gc2NhdHRlcgpzYW1wbGUgPC0gcnVuaWYobiwgbWluPTAsIG1heD1OKQp0aXRsZSA8LSAnTG9jYXRpb25zIG9mIFBhbGluZHJvbWVzIChPcmlnaW5hbCB2cy4gU2ltdWxhdGVkKScKeC5heGlzIDwtICdCYXNlIFBhaXInCmJpbnMgPC0gMzUKaGlzdChsb2NhdGlvbnMsIGJyZWFrcz1iaW5zLCBwcm9iYWJpbGl0eT1UUlVFLCBjb2w9cmdiKDEsMCwwLDAuNSksIG1haW49dGl0bGUsIHhsYWI9eC5heGlzKQpsaW5lcyhkZW5zaXR5KGxvY2F0aW9ucywgYWRqdXN0PTIpLCBjb2w9MikKaGlzdChzYW1wbGUsIGJyZWFrcz1iaW5zLCBwcm9iYWJpbGl0eT1UUlVFLCBjb2w9cmdiKDAsMCwxLDAuNSksIGFkZD1UUlVFKQpsaW5lcyhkZW5zaXR5KHNhbXBsZSwgYWRqdXN0PTIpLCBjb2w9NCkKbGVnZW5kKCd0b3ByaWdodCcsIGxlZ2VuZD1jKCdPcmlnaW5hbCcsICdVbmlmb3JtJyksIGx0eT1jKDEsMSksIGNvbD1jKHJnYigxLDAsMCwwLjUpLCByZ2IoMCwwLDEsMC41KSkpCgojIFNpbmdsZSBQYWxpbmRyb21lIFNwYWNpbmcKbG9jYXRpb25zLnNvcnRlZCA9IHNvcnQobG9jYXRpb25zLCBkZWNyZWFzaW5nID0gRkFMU0UpCmRpc3RhbmNlLnNpbmdsZSA8LSBhYnMobG9jYXRpb25zLnNvcnRlZFstMV0tbG9jYXRpb25zLnNvcnRlZFstbGVuZ3RoKGxvY2F0aW9ucy5zb3J0ZWQpXSkKCiMgSGlzdG9ncmFtIG9mIHNwYWNpbmdzIG9mIHBhbGluZHJvbWVzIGluIG9yaWdpbmFsIGRhdGEgYW5kIGV4cG9uZW50aWFsIGRpc3RyaWJ1dGlvbgpoaXN0KGRpc3RhbmNlLnNpbmdsZSwgYnJlYWtzPSAxNSwgY29sID0gcmdiKDEsMCwwLDAuNSksIHByb2JhYmlsaXR5ID0gVFJVRSwgbWFpbiA9ICJDb25zZWN1dGl2ZSBwYWxpbmRyb21lIFNwYWNpbmdzIERpc3RyaWJ1dGlvbiBDb21wYXJpc29uIiwgeGxhYiA9ICJEaXN0YW5jZSBiZXR3ZWVuIENvbnNlY3V0aXZlIFBhbGluZHJvbWVzIiwgeWxpbSA9IGMoMCwwLjAwMSkpCmxpbmVzKGRlbnNpdHkoZGlzdGFuY2Uuc2luZ2xlLCBhZGp1c3QgPSAyKSwgY29sID0gcmdiKDEsMCwwLDAuNSkpCkV4cG8gPC0gcmV4cChuLTEsIHJhdGUgPSAxL21lYW4oZGlzdGFuY2Uuc2luZ2xlKSkKaGlzdChFeHBvLCBicmVha3MgPSAxNSwgY29sID0gcmdiKDAsMCwxLDAuNSksIHByb2JhYmlsaXR5ID0gVFJVRSwgYWRkID0gVFJVRSkKbGluZXMoZGVuc2l0eShFeHBvLCBhZGp1c3QgPSAyKSwgY29sID0gcmdiKDAsMCwxLDAuNSkpCmxlZ2VuZCh4ID0gNDIwMCwgeSA9IDAuMDAwOSwgbGVnZW5kID0gYygiU2FtcGxlIiwgIkV4cG9uZW50aWFsIiksIGx0eSA9IGMoMSwxKSwgY29sID0gYyhyZ2IoMSwwLDAsMC41KSwgcmdiKDAsMCwxLDAuNSkpKQoKIyBDaGktc3F1YXJlIEdvb2RuZXNzIG9mIEZpdCBUZXN0CiMgQ2FzZSAxOiBEaXZpZGVkIGluIDcgaW50ZXJ2YWxzCiMgQ29uc3RydWN0IGV4cGVjdGVkIG51bWJlciBvZiBpbnRlcnZhbHMKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gc29ydChkaXN0YW5jZS5zaW5nbGUsIGRlY3JlYXNpbmcgPSBGQUxTRSkKbGFtYmRhIDwtIDEvbWVhbihkaXN0YW5jZS5zaW5nbGUpCnNwYWNpbmdzLmludGVydmFscyA8LSBhcy5udW1lcmljKHF1YW50aWxlKHNwYWNpbmdzLm9ic2VydmVkLCBwcm9icyA9IGMoMCwwLjA1LCAwLjEsIDAuMywgMC41LCAwLjcsIDAuOSwxKSkpCgpzcGFjaW5ncy5leHBlY3RlZCA8LSAobi0xKSooZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy1sZW5ndGgoc3BhY2luZ3MuaW50ZXJ2YWxzKV0pLWV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stMV0pKQpzcGFjaW5ncy5leHBlY3RlZFtsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpXSA8LSBuLXN1bShzcGFjaW5ncy5leHBlY3RlZFsxOmxlbmd0aChzcGFjaW5ncy5leHBlY3RlZCktMV0pCnNwYWNpbmdzLm9ic2VydmVkIDwtIGFzLm51bWVyaWModGFibGUoY3V0KGRpc3RhbmNlLnNpbmdsZSwgYnJlYWtzPXNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3Q9VFJVRSkpKQpjb250aW5nZW5jeV83IDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfNwoKY2hpXzIgPC0gc3VtKChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKV4yL3NwYWNpbmdzLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHA9MC45NSwgZGY9NSkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmPTUsIGxvd2VyLnRhaWw9RkFMU0UpCnByaW50KHBhc3RlKCdUaGUgcF92YWx1ZSB3aGVuIHRoZSBkaXN0YW5jZSBpcyBzcGxpdGVkIGludG8gNyBzdWItaW50ZXJ2YWxzIGlzJywgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCkgLyBzcXJ0KHNwYWNpbmdzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZT0naCcsIHlsYWI9J1N0YW5kYXJkaXplZCBSZXNpZHVhbHMnLCB4bGFiPSdQYWxpbmRyb21lIGxvY2F0aW9ucycsIG1haW49J1Bsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gNyBiaW5zKScpCgojIENhc2UgMjogRGl2aWRlZCBpbiAxMCBpbnRlcnZhbHMKIyBDb25zdHJ1Y3QgZXhwZWN0ZWQgbnVtYmVyIG9mIGludGVydmFscwpzcGFjaW5ncy5vYnNlcnZlZCA8LSBzb3J0KGRpc3RhbmNlLnNpbmdsZSwgZGVjcmVhc2luZyA9IEZBTFNFKQpzcGFjaW5ncy5pbnRlcnZhbHMgPC0gYXMubnVtZXJpYyhxdWFudGlsZShzcGFjaW5ncy5vYnNlcnZlZCwgcHJvYnMgPSBjKHNlcSgwLDEsIGJ5ID0gMC4xKSkpKQoKc3BhY2luZ3MuZXhwZWN0ZWQgPC0gKG4tMSkqKGV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stbGVuZ3RoKHNwYWNpbmdzLmludGVydmFscyldKS1leHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLTFdKSkKc3BhY2luZ3MuZXhwZWN0ZWRbbGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKV0gPC0gbi1zdW0oc3BhY2luZ3MuZXhwZWN0ZWRbMTpsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpLTFdKQpzcGFjaW5ncy5vYnNlcnZlZCA8LSBhcy5udW1lcmljKHRhYmxlKGN1dChkaXN0YW5jZS5zaW5nbGUsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmNvbnRpbmdlbmN5XzEwIDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfMTAKCmNoaV8yIDwtIHN1bSgoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCleMi9zcGFjaW5ncy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwPTAuOTUsIGRmPTgpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZj04LCBsb3dlci50YWlsPUZBTFNFKQpwcmludChwYXN0ZSgnVGhlIHBfdmFsdWUgd2hlbiB0aGUgZGlzdGFuY2UgaXMgc3BsaXRlZCBpbnRvIDEwIHN1Yi1pbnRlcnZhbHMgaXMnLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKSAvIHNxcnQoc3BhY2luZ3MuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlPSdoJywgeWxhYj0nU3RhbmRhcmRpemVkIFJlc2lkdWFscycsIHhsYWI9J1BhbGluZHJvbWUgbG9jYXRpb25zJywgbWFpbj0nUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIExvY2F0aW9ucyAoZGl2aWRlZCBpbiAxMCBiaW5zKScpCgojIENhc2UgMzogRGl2aWRlZCBpbiAyMCBpbnRlcnZhbHMKIyBDb25zdHJ1Y3QgZXhwZWN0ZWQgbnVtYmVyIG9mIGludGVydmFscwpzcGFjaW5ncy5vYnNlcnZlZCA8LSBzb3J0KGRpc3RhbmNlLnNpbmdsZSwgZGVjcmVhc2luZyA9IEZBTFNFKQpzcGFjaW5ncy5pbnRlcnZhbHMgPC0gYXMubnVtZXJpYyhxdWFudGlsZShzcGFjaW5ncy5vYnNlcnZlZCwgcHJvYnMgPSBjKHNlcSgwLDEsIGJ5ID0gMC4wNSkpKSkKCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTEpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2Uuc2luZ2xlLCBicmVha3MgPSBzcGFjaW5ncy5pbnRlcnZhbHMsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpKQoKY29udGluZ2VuY3lfMjAgPC0gZGF0YS5mcmFtZShzcGFjaW5ncy5pbnRlcnZhbHNbLTFdLHNwYWNpbmdzLm9ic2VydmVkLHNwYWNpbmdzLmV4cGVjdGVkKQpjb250aW5nZW5jeV8yMAoKY2hpXzIgPC0gc3VtKChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKV4yL3NwYWNpbmdzLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHA9MC45NSwgZGY9MTgpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZj0xOCwgbG93ZXIudGFpbD1GQUxTRSkKcHJpbnQocGFzdGUoJ1RoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byAyMCBzdWItaW50ZXJ2YWxzIGlzJywgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCkgLyBzcXJ0KHNwYWNpbmdzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZT0naCcsIHlsYWI9J1N0YW5kYXJkaXplZCBSZXNpZHVhbHMnLCB4bGFiPSdQYWxpbmRyb21lIGxvY2F0aW9ucycsIG1haW49J1Bsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gMjAgYmlucyknKQoKIyBDb25zZWN1dGl2ZSBQYWlycwpsb2NhdGlvbnMuc29ydGVkIDwtICBzb3J0KGxvY2F0aW9ucywgZGVjcmVhc2luZyA9IEZBTFNFKQpsb2NhdGlvbnMucGFpcnMgPC0gbG9jYXRpb25zLnNvcnRlZFstbGVuZ3RoKGxvY2F0aW9ucy5zb3J0ZWQpXQpkaXN0YW5jZS5wYWlycyA8LSBhYnMobG9jYXRpb25zLnNvcnRlZFstMV1bLTFdLWxvY2F0aW9ucy5wYWlyc1stbGVuZ3RoKGxvY2F0aW9ucy5wYWlycyldKQoKIyBIaXN0b2dyYW0gb2Ygc3BhY2luZ3Mgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uCmhpc3QoZGlzdGFuY2UucGFpcnMsIGJyZWFrcz0gMTUsIGNvbCA9IHJnYigxLDAsMCwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiQ29uc2VjdXRpdmUgUGFpcnMgU3BhY2luZ3MgRGlzdHJpYnV0aW9uIENvbXBhcmlzb24iLCB4bGFiID0gIkRpc3RhbmNlIGJldHdlZW4gQ29uc2VjdXRpdmUgUGFpcnMgb2YgUGFsaW5kcm9tZXMgTG9jYXRpb25zIiwgeWxpbSA9IGMoMCwwLjAwMSkpCmxpbmVzKGRlbnNpdHkoZGlzdGFuY2UucGFpcnMsIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMSwwLDAsMC41KSkKRXhwbyA8LSByZXhwKG4tMiwgcmF0ZSA9IDEvbWVhbihkaXN0YW5jZS5wYWlycykpCmhpc3QoRXhwbywgYnJlYWtzID0gMTUsIGNvbCA9IHJnYigwLDAsMSwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGFkZCA9IFRSVUUpCmxpbmVzKGRlbnNpdHkoRXhwbywgYWRqdXN0ID0gMiksIGNvbCA9IHJnYigwLDAsMSwwLjUpKQpsZWdlbmQoeCA9IDQyMDAsIHkgPSAwLjAwMDUsIGxlZ2VuZCA9IGMoIlNhbXBsZSIsICJFeHBvbmVudGlhbCIpLCBsdHkgPSBjKDEsMSksIGNvbCA9IGMocmdiKDEsMCwwLDAuNSksIHJnYigwLDAsMSwwLjUpKSkKCiMgQ2hpLXNxdWFyZSBHb29kbmVzcyBvZiBGaXQgVGVzdAojIENhc2UgMTogRGl2aWRlZCBpbiA3IGludGVydmFscwojIENvbnN0cnVjdCBleHBlY3RlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCnNwYWNpbmdzLm9ic2VydmVkIDwtIHNvcnQoZGlzdGFuY2UucGFpcnMsIGRlY3JlYXNpbmcgPSBGQUxTRSkKbGFtYmRhIDwtIDEvbWVhbihkaXN0YW5jZS5wYWlycykKc3BhY2luZ3MuaW50ZXJ2YWxzIDwtIGFzLm51bWVyaWMocXVhbnRpbGUoc3BhY2luZ3Mub2JzZXJ2ZWQsIHByb2JzID0gYygwLDAuMDUsIDAuMSwgMC4zLCAwLjUsIDAuNywgMC45LDEpKSkKc3BhY2luZ3MuZXhwZWN0ZWQgPC0gKG4tMikqKGV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stbGVuZ3RoKHNwYWNpbmdzLmludGVydmFscyldKS1leHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLTFdKSkKc3BhY2luZ3MuZXhwZWN0ZWRbbGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKV0gPC0gbi1zdW0oc3BhY2luZ3MuZXhwZWN0ZWRbMTpsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpLTFdKQpzcGFjaW5ncy5vYnNlcnZlZCA8LSBhcy5udW1lcmljKHRhYmxlKGN1dChkaXN0YW5jZS5wYWlycywgYnJlYWtzID0gc3BhY2luZ3MuaW50ZXJ2YWxzLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSkKCmNvbnRpbmdlbmN5XzcgPC0gZGF0YS5mcmFtZShzcGFjaW5ncy5pbnRlcnZhbHNbLTFdLHNwYWNpbmdzLm9ic2VydmVkLHNwYWNpbmdzLmV4cGVjdGVkKQpjb250aW5nZW5jeV83CgpjaGlfMiA8LSBzdW0oKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpXjIvc3BhY2luZ3MuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocD0wLjk1LCBkZj01KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGY9NSwgbG93ZXIudGFpbD1GQUxTRSkKcHJpbnQocGFzdGUoJ1RoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byA3IHN1Yi1pbnRlcnZhbHMgaXMnLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKSAvIHNxcnQoc3BhY2luZ3MuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlPSdoJywgeWxhYj0nU3RhbmRhcmRpemVkIFJlc2lkdWFscycsIHhsYWI9J1NwYWNpbmdzIGJldHdlZW4gUGFsaW5kcm9tZSBQYWlycycsIG1haW49J1Bsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gNyBiaW5zKScpCgojIENhc2UgMjogRGl2aWRlZCBpbiAxMCBpbnRlcnZhbHMKIyBDb25zdHJ1Y3QgZXhwZWN0ZWQgbnVtYmVyIG9mIGludGVydmFscwpzcGFjaW5ncy5vYnNlcnZlZCA8LSBzb3J0KGRpc3RhbmNlLnBhaXJzLCBkZWNyZWFzaW5nID0gRkFMU0UpCnNwYWNpbmdzLmludGVydmFscyA8LSBhcy5udW1lcmljKHF1YW50aWxlKHNwYWNpbmdzLm9ic2VydmVkLCBwcm9icyA9IGMoc2VxKDAsMSwgYnkgPSAwLjEpKSkpCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTIpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2UucGFpcnMsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCgpjb250aW5nZW5jeV8xMCA8LSBkYXRhLmZyYW1lKHNwYWNpbmdzLmludGVydmFsc1stMV0sc3BhY2luZ3Mub2JzZXJ2ZWQsc3BhY2luZ3MuZXhwZWN0ZWQpCmNvbnRpbmdlbmN5XzEwCgpjaGlfMiA8LSBzdW0oKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpXjIvc3BhY2luZ3MuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocD0wLjk1LCBkZj04KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGY9OCwgbG93ZXIudGFpbD1GQUxTRSkKcHJpbnQocGFzdGUoJ1RoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byAxMCBzdWItaW50ZXJ2YWxzIGlzJywgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCkgLyBzcXJ0KHNwYWNpbmdzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZT0naCcsIHlsYWI9J1N0YW5kYXJkaXplZCBSZXNpZHVhbHMnLCB4bGFiPSdTcGFjaW5ncyBiZXR3ZWVuIFBhbGluZHJvbWUgUGFpcnMnLCBtYWluPSdQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgTG9jYXRpb25zIChkaXZpZGVkIGluIDEwIGJpbnMpJykKCiMgQ2FzZSAzOiBEaXZpZGVkIGluIDIwIGludGVydmFscwojIENvbnN0cnVjdCBleHBlY3RlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCnNwYWNpbmdzLm9ic2VydmVkIDwtIHNvcnQoZGlzdGFuY2UucGFpcnMsIGRlY3JlYXNpbmcgPSBGQUxTRSkKc3BhY2luZ3MuaW50ZXJ2YWxzIDwtIGFzLm51bWVyaWMocXVhbnRpbGUoc3BhY2luZ3Mub2JzZXJ2ZWQsIHByb2JzID0gYyhzZXEoMCwxLCBieSA9IDAuMDUpKSkpCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTIpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2UucGFpcnMsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCgpjb250aW5nZW5jeV8yMCA8LSBkYXRhLmZyYW1lKHNwYWNpbmdzLmludGVydmFsc1stMV0sc3BhY2luZ3Mub2JzZXJ2ZWQsc3BhY2luZ3MuZXhwZWN0ZWQpCmNvbnRpbmdlbmN5XzIwCgpjaGlfMiA8LSBzdW0oKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpXjIvc3BhY2luZ3MuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocD0wLjk1LCBkZj0xOCkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmPTE4LCBsb3dlci50YWlsPUZBTFNFKQpwcmludChwYXN0ZSgnVGhlIHBfdmFsdWUgd2hlbiB0aGUgZGlzdGFuY2UgaXMgc3BsaXRlZCBpbnRvIDIwIHN1Yi1pbnRlcnZhbHMgaXMnLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKSAvIHNxcnQoc3BhY2luZ3MuZXhwZWN0ZWQpCgpwbG90KFJlc2lkdWFscywgdHlwZSA9ICdoJywgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJTcGFjaW5ncyBiZXR3ZWVuIFBhbGluZHJvbWUgUGFpcnMiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gMjAgYmlucykiKQoKIyBDb25zZWN1dGl2ZSBUcmlwbGV0cwpsb2NhdGlvbnMuc29ydGVkIDwtICBzb3J0KGxvY2F0aW9ucywgZGVjcmVhc2luZyA9IEZBTFNFKQpsb2NhdGlvbnMudHJpcGxldHMgPC0gbG9jYXRpb25zLnNvcnRlZFstbGVuZ3RoKGxvY2F0aW9ucy5zb3J0ZWQpXQpsb2NhdGlvbnMudHJpcGxldHMgPC0gbG9jYXRpb25zLnRyaXBsZXRzWy1sZW5ndGgobG9jYXRpb25zLnRyaXBsZXRzKV0KZGlzdGFuY2UudHJpcGxldHMgPC0gYWJzKGxvY2F0aW9ucy5zb3J0ZWRbLTFdWy0xXVstMV0tbG9jYXRpb25zLnRyaXBsZXRzWy1sZW5ndGgobG9jYXRpb25zLnRyaXBsZXRzKV0pCgojIEhpc3RvZ3JhbSBvZiBzcGFjaW5ncyBvZiBwYWxpbmRyb21lcyBpbiBvcmlnaW5hbCBkYXRhIGFuZCBleHBvbmVudGlhbCBkaXN0cmlidXRpb24KaGlzdChkaXN0YW5jZS50cmlwbGV0cywgYnJlYWtzPSAxNSwgY29sID0gcmdiKDEsMCwwLDAuNSksIHByb2JhYmlsaXR5ID0gVFJVRSwgbWFpbiA9ICJDb25zZWN1dGl2ZSBUcmlwbGV0cyBTcGFjaW5ncyBEaXN0cmlidXRpb24gQ29tcGFyaXNvbiIsIHhsYWIgPSAiRGlzdGFuY2UgYmV0d2VlbiBDb25zZWN1dGl2ZSBQYWxpbmRyb21lcyBUcmlwbGV0cyIsIHlsaW0gPSBjKDAsMC4wMDA0KSkKbGluZXMoZGVuc2l0eShkaXN0YW5jZS50cmlwbGV0cywgYWRqdXN0ID0gMiksIGNvbCA9IHJnYigxLDAsMCwwLjUpKQpHYW0gPC0gcmdhbW1hKG4tMiwgMiwgcmF0ZSA9IDEvbWVhbihkaXN0YW5jZS5wYWlycykpCmhpc3QoR2FtLCBicmVha3MgPSAxNSwgY29sID0gcmdiKDAsMCwxLDAuNSksIHByb2JhYmlsaXR5ID0gVFJVRSwgYWRkID0gVFJVRSkKbGluZXMoZGVuc2l0eShHYW0sIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMCwwLDEsMC41KSkKbGVnZW5kKHggPSA1MDAwLCB5ID0gMC4wMDAzLCBsZWdlbmQgPSBjKCJTYW1wbGUiLCAiR2FtbWEiKSwgbHR5ID0gYygxLDEpLCBjb2wgPSBjKHJnYigxLDAsMCwwLjUpLCByZ2IoMCwwLDEsMC41KSkpCgojIENoaS1zcXVhcmUgR29vZG5lc3Mgb2YgRml0IFRlc3QgKE5lZWQgdG8gYmUgY2hhbmdlZCkKIyBDYXNlIDE6IERpdmlkZWQgaW4gNyBpbnRlcnZhbHMKIyBDb25zdHJ1Y3QgZXhwZWN0ZWQgbnVtYmVyIG9mIGludGVydmFscwpzcGFjaW5ncy5vYnNlcnZlZCA8LSBzb3J0KGRpc3RhbmNlLnRyaXBsZXRzLCBkZWNyZWFzaW5nID0gRkFMU0UpCmxhbWJkYSA8LSAyL21lYW4oZGlzdGFuY2UucGFpcnMpCnNwYWNpbmdzLmludGVydmFscyA8LSBhcy5udW1lcmljKHF1YW50aWxlKHNwYWNpbmdzLm9ic2VydmVkLCBwcm9icyA9IGMoMCwwLjA1LCAwLjEsIDAuMywgMC41LCAwLjcsIDAuOSwxKSkpCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTMpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2UucGFpcnMsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmNvbnRpbmdlbmN5XzcgPC0gZGF0YS5mcmFtZShzcGFjaW5ncy5pbnRlcnZhbHNbLTFdLHNwYWNpbmdzLm9ic2VydmVkLHNwYWNpbmdzLmV4cGVjdGVkKQpjb250aW5nZW5jeV83CgpjaGlfMiA8LSBzdW0oKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpXjIvc3BhY2luZ3MuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocCA9IDAuOTUsIGRmID0gNSkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gNSwgbG93ZXIudGFpbCA9IEZBTFNFKQpwcmludChwYXN0ZSgiVGhlIHBfdmFsdWUgd2hlbiB0aGUgZGlzdGFuY2UgaXMgc3BsaXRlZCBpbnRvIDcgc3ViLWludGVydmFscyBpcyIsIHBfdmFsdWUpKQojIyBWaXN1YWxpemF0aW9uIG9mIHRoZSBSZXNpZHVhbApSZXNpZHVhbHMgPC0gKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpIC8gc3FydChzcGFjaW5ncy5leHBlY3RlZCkKcGxvdChSZXNpZHVhbHMsIHR5cGUgPSAnaCcsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUGFsaW5kcm9tZSBsb2NhdGlvbnMiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gNyBiaW5zKSIpCgojIENhc2UgMjogRGl2aWRlZCBpbiAxMCBpbnRlcnZhbHMKIyBDb25zdHJ1Y3QgZXhwZWN0ZWQgbnVtYmVyIG9mIGludGVydmFscwpzcGFjaW5ncy5vYnNlcnZlZCA8LSBzb3J0KGRpc3RhbmNlLnBhaXJzLCBkZWNyZWFzaW5nID0gRkFMU0UpCnNwYWNpbmdzLmludGVydmFscyA8LSBhcy5udW1lcmljKHF1YW50aWxlKHNwYWNpbmdzLm9ic2VydmVkLCBwcm9icyA9IGMoc2VxKDAsMSwgYnkgPSAwLjEpKSkpCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTMpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2UucGFpcnMsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmNvbnRpbmdlbmN5XzEwIDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfMTAKCmNoaV8yIDwtIHN1bSgoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCleMi9zcGFjaW5ncy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSA4KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGYgPSA4LCBsb3dlci50YWlsID0gRkFMU0UpCnByaW50KHBhc3RlKCJUaGUgcF92YWx1ZSB3aGVuIHRoZSBkaXN0YW5jZSBpcyBzcGxpdGVkIGludG8gMTAgc3ViLWludGVydmFscyBpcyIsIHBfdmFsdWUpKQojIyBWaXN1YWxpemF0aW9uIG9mIHRoZSBSZXNpZHVhbApSZXNpZHVhbHMgPC0gKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpIC8gc3FydChzcGFjaW5ncy5leHBlY3RlZCkKcGxvdChSZXNpZHVhbHMsIHR5cGUgPSAnaCcsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUGFsaW5kcm9tZSBsb2NhdGlvbnMiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gMTAgYmlucykiKQoKIyBDYXNlIDM6IERpdmlkZWQgaW4gMjAgaW50ZXJ2YWxzCiMgQ29uc3RydWN0IGV4cGVjdGVkIG51bWJlciBvZiBpbnRlcnZhbHMKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gc29ydChkaXN0YW5jZS5wYWlycywgZGVjcmVhc2luZyA9IEZBTFNFKQpzcGFjaW5ncy5pbnRlcnZhbHMgPC0gYXMubnVtZXJpYyhxdWFudGlsZShzcGFjaW5ncy5vYnNlcnZlZCwgcHJvYnMgPSBjKHNlcSgwLDEsIGJ5ID0gMC4wNSkpKSkKc3BhY2luZ3MuZXhwZWN0ZWQgPC0gKG4tMykqKGV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stbGVuZ3RoKHNwYWNpbmdzLmludGVydmFscyldKS1leHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLTFdKSkKc3BhY2luZ3MuZXhwZWN0ZWRbbGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKV0gPC0gbi1zdW0oc3BhY2luZ3MuZXhwZWN0ZWRbMTpsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpLTFdKQpzcGFjaW5ncy5vYnNlcnZlZCA8LSBhcy5udW1lcmljKHRhYmxlKGN1dChkaXN0YW5jZS5wYWlycywgYnJlYWtzID0gc3BhY2luZ3MuaW50ZXJ2YWxzLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSkKY29udGluZ2VuY3lfMjAgPC0gZGF0YS5mcmFtZShzcGFjaW5ncy5pbnRlcnZhbHNbLTFdLHNwYWNpbmdzLm9ic2VydmVkLHNwYWNpbmdzLmV4cGVjdGVkKQpjb250aW5nZW5jeV8yMAoKY2hpXzIgPC0gc3VtKChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKV4yL3NwYWNpbmdzLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHAgPSAwLjk1LCBkZiA9IDE4KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGYgPSAxOCwgbG93ZXIudGFpbCA9IEZBTFNFKQpwcmludChwYXN0ZSgiVGhlIHBfdmFsdWUgd2hlbiB0aGUgZGlzdGFuY2UgaXMgc3BsaXRlZCBpbnRvIDIwIHN1Yi1pbnRlcnZhbHMgaXMiLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKSAvIHNxcnQoc3BhY2luZ3MuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlID0gJ2gnLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlBhbGluZHJvbWUgbG9jYXRpb25zIiwgbWFpbiA9ICJQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgTG9jYXRpb25zIChkaXZpZGVkIGluIDIwIGJpbnMpIikKYGBgCgoKIyMgU2NlbmFyaW8gMzogQ291bnRzClVzZSBncmFwaGljYWwgbWV0aG9kcyBhbmQgbW9yZSBmb3JtYWwgc3RhdGlzdGljYWwgdGVzdHMgdG8gZXhhbWluZSB0aGUgY291bnRzIG9mIHBhbGluZHJvbWVzIGluIHZhcmlvdXMgcmVnaW9ucyBvZiB0aGUgRE5BLiBTcGxpdCB0aGUgRE5BIGludG8gbm9ub3ZlcmxhcHBpbmcgcmVnaW9ucyBvZiBlcXVhbCBsZW5ndGggdG8gY29tcGFyZSB0aGUgbnVtYmVyIG9mIHBhbGluZG9tcmVzIGluIGFuIGludGVydmFsIHRvIHRoZSBudW1iZXIgb2YgdGhhdCB5b3Ugd291bGQgZXhwZWN0IGZyb20gdW5pZm9ybSByYW5kb20gc2NhdHRlci4gVGhlIGNvdW50cyBmb3Igc2hvcnRlciByZWdpb25zIHdpbGwgYmUgbW9yZSB2YXJpYWJsZSB0aGFuIHRob3NlIGZvciBsb2duZXIgcmVnaW9ucy4gQWxzbywgY29uc2lkZXIgY2xhc3NpZnlpbmcgdGhlIHJlZ2lvbnMgYWNjb3JkaW5nIHRvIHRoZSBudW1iZXIgb2YgY291bnRzLgoKYGBge3J9CnJlZ2lvbnNwbGl0IDwtIGZ1bmN0aW9uKG4ucmVnaW9uLCBnZW5lLCBzaXRlKXsKICBjb3VudC5pbnQgPC0gdGFibGUoY3V0KHNpdGUsIGJyZWFrcyA9IHNlcSgxLCBsZW5ndGgoZ2VuZSksIGxlbmd0aC5vdXQ9bi5yZWdpb24rMSksIGluY2x1ZGUubG93ZXN0PVRSVUUpKQogIGNvdW50LnZlY3RvciA8LSBhcy52ZWN0b3IoY291bnQuaW50KQogIGNvdW50LnRhYiA8LSB0YWJsZShjb3VudC52ZWN0b3IpCiAgcmV0dXJuIChjb3VudC50YWIpCn0KYGBgCgpgYGB7cn0KIyBDYXNlIDE6IGRpdmlkZWQgYnkgNDAgaW50ZXJ2YWxzCm4ucmVnaW9uIDwtIDQwCmdlbmUgPC0gc2VxKDEsTikKb2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyhyZWdpb25zcGxpdChuLnJlZ2lvbiwgZ2VuZSwgbG9jYXRpb25zKSkKaW50ZXJ2YWwgPC0gYXMubnVtZXJpYyhuYW1lcyhyZWdpb25zcGxpdChuLnJlZ2lvbiwgZ2VuZSwgbG9jYXRpb25zKSkpCmxhbWJkYSA8LSBuL24ucmVnaW9uCgojIEhpc3RvZ3JhbSBvZiBjb3VudHMgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgcG9pc3NvbiBkaXN0cmlidXRpb24KY291bnRzIDwtIGFzLnZlY3Rvcih0YWJsZShjdXQobG9jYXRpb25zLCBicmVha3MgPSBzZXEoMCwgTiwgbGVuZ3RoLm91dCA9IG4ucmVnaW9uKzEpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSkKaGlzdChjb3VudHMsIGJyZWFrcyA9IGJpbnMsIGNvbCA9IHJnYigxLDAsMCwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiQ291bnRzIERpc3RyaWJ1dGlvbiBDb21wYXJpc29uICg0MCBTdWItaW50ZXJ2YWxzKSIsIHhsYWIgPSAiTnVtYmVyIG9mIFBhbGluZHJvbWVzIFNpdGVzIEluc2lkZSBhbiBJbnRlcnZhbCIsIHlsaW0gPSBjKDAsMC40KSkKbGluZXMoZGVuc2l0eShjb3VudHMsIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMSwwLDAsMC41KSkKUG9pcyA8LSBycG9pcyhuLCBsYW1iZGEpCmhpc3QoUG9pcywgYnJlYWtzID0gMTUsIGNvbCA9IHJnYigwLDAsMSwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGFkZCA9IFRSVUUpCmxpbmVzKGRlbnNpdHkoUG9pcywgYWRqdXN0ID0gMiksIGNvbCA9IHJnYigwLDAsMSwwLjUpKQpsZWdlbmQoeCA9IDE4LCB5ID0gMC4xNywgbGVnZW5kID0gYygiU2FtcGxlIiwgIlBvaXNzb24iKSwgbHR5ID0gYygxLDEpLCBjb2wgPSBjKHJnYigxLDAsMCwwLjUpLCByZ2IoMCwwLDEsMC41KSkpCgoKIyBDaGktc3FyIEdvb2RuZXNzIG9mIEZpdCB0ZXN0CmV4cGVjdGVkIDwtIG4ucmVnaW9uKmV4cCgtbGFtYmRhKSogbGFtYmRhKiooaW50ZXJ2YWwpL2ZhY3RvcmlhbChpbnRlcnZhbCkKZm9yIChpIGluIGMoMDoyKSl7CiAgZXhwZWN0IDwtIG4ucmVnaW9uKmV4cCgtbGFtYmRhKSogbGFtYmRhKiooaSkvZmFjdG9yaWFsKGkpCiAgZXhwZWN0ZWRbMV0gPC0gZXhwZWN0ZWRbMV0rIGV4cGVjdAp9CmV4cGVjdCA8LSBuLnJlZ2lvbipleHAoLWxhbWJkYSkqIGxhbWJkYSoqKDExKS9mYWN0b3JpYWwoMTEpCmV4cGVjdGVkWzhdIDwtIGV4cGVjdGVkWzhdKyBleHBlY3QKZXhwZWN0ZWRbMTBdIDwtIDAKZm9yIChpIGluIGMoMToxMikpewogIGV4cGVjdCA8LSBleHAoLWxhbWJkYSkqIGxhbWJkYSoqKGkpL2ZhY3RvcmlhbChpKQogIGV4cGVjdGVkWzEwXSA8LSBleHBlY3RlZFsxMF0rIGV4cGVjdAp9CmV4cGVjdGVkWzEwXSA8LSAoMS1leHBlY3RlZFsxMF0pKm4ucmVnaW9uCmNvdW50cy5leHBlY3RlZCA8LSBjKCkKY291bnRzLmludGVydmFsIDwtIGMoKQpjb3VudHMub2JzZXJ2ZWQgPC0gYygpCiMgR3JvdXAgYmlucwpjb3VudHMuZXhwZWN0ZWRbMV0gPC0gc3VtKGV4cGVjdGVkWzE6Ml0pCmNvdW50cy5leHBlY3RlZFsyXSA8LSBzdW0oZXhwZWN0ZWRbMzo0XSkKY291bnRzLmV4cGVjdGVkWzNdIDwtIHN1bShleHBlY3RlZFs1XSkKY291bnRzLmV4cGVjdGVkWzRdIDwtIHN1bShleHBlY3RlZFs2OjddKQpjb3VudHMuZXhwZWN0ZWRbNV0gPC0gc3VtKGV4cGVjdGVkWzg6MTBdKQpjb3VudHMub2JzZXJ2ZWRbMV0gPC0gc3VtKG9ic2VydmVkWzE6Ml0pCmNvdW50cy5vYnNlcnZlZFsyXSA8LSBzdW0ob2JzZXJ2ZWRbMzo0XSkKY291bnRzLm9ic2VydmVkWzNdIDwtIHN1bShvYnNlcnZlZFs1XSkKY291bnRzLm9ic2VydmVkWzRdIDwtIHN1bShvYnNlcnZlZFs2OjddKQpjb3VudHMub2JzZXJ2ZWRbNV0gPC0gc3VtKG9ic2VydmVkWzg6MTBdKQpjb3VudHMuaW50ZXJ2YWxbMV0gPC0gaW50ZXJ2YWxbMl0KY291bnRzLmludGVydmFsWzJdIDwtIGludGVydmFsWzRdCmNvdW50cy5pbnRlcnZhbFszXSA8LSBpbnRlcnZhbFs1XQpjb3VudHMuaW50ZXJ2YWxbNF0gPC0gaW50ZXJ2YWxbN10KY291bnRzLmludGVydmFsWzVdIDwtIGludGVydmFsWzddKzEKY291bnRzLnRhYmxlNDAgPC0gZGF0YS5mcmFtZShjb3VudHMuaW50ZXJ2YWwsY291bnRzLm9ic2VydmVkLGNvdW50cy5leHBlY3RlZCkKY291bnRzLnRhYmxlNDAKIyBDaGktc3F1YXJlIHN0YXRpc3RpYwpjaGlfMiA8LSBzdW0oKGNvdW50cy5vYnNlcnZlZCAtIGNvdW50cy5leHBlY3RlZCleMi9jb3VudHMuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocCA9IDAuOTUsIGRmID0gMykKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gMywgbG93ZXIudGFpbCA9IEZBTFNFKQpwcmludChwYXN0ZSgiVGhlIHBfdmFsdWUgd2hlbiB0aGUgZGlzdGFuY2UgaXMgc3BsaXRlZCBpbnRvIDQwIHN1Yi1pbnRlcnZhbHMgaXMiLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChjb3VudHMub2JzZXJ2ZWQgLSBjb3VudHMuZXhwZWN0ZWQpIC8gc3FydChjb3VudHMuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlID0gJ2gnLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlBhbGluZHJvbWUgY291bnRzIiwgbWFpbiA9ICJQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgQ291bnRzIChkaXZpZGVkIGluIDQwIHN1Yi1pbnRlcnZhbHMpIikKCmBgYApgYGB7cn0KIyBDYXNlIDI6IGRpdmlkZWQgYnkgNjAgaW50ZXJ2YWxzCm4ucmVnaW9uIDwtIDYwCmdlbmUgPC0gc2VxKDEsTikKCm9ic2VydmVkIDwtIGFzLm51bWVyaWMocmVnaW9uc3BsaXQobi5yZWdpb24sIGdlbmUsIGxvY2F0aW9ucykpCmludGVydmFsIDwtIGFzLm51bWVyaWMobmFtZXMocmVnaW9uc3BsaXQobi5yZWdpb24sIGdlbmUsIGxvY2F0aW9ucykpKQpsYW1iZGEgPC0gbi9uLnJlZ2lvbgoKIyBIaXN0b2dyYW0gb2YgY291bnRzIG9mIHBhbGluZHJvbWVzIGluIG9yaWdpbmFsIGRhdGEgYW5kIHBvaXNzb24gZGlzdHJpYnV0aW9uCmNvdW50cyA8LSBhcy52ZWN0b3IodGFibGUoY3V0KGxvY2F0aW9ucywgYnJlYWtzID0gc2VxKDAsIE4sIGxlbmd0aC5vdXQgPSBuLnJlZ2lvbisxKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmhpc3QoY291bnRzLCBicmVha3MgPSBiaW5zLCBjb2wgPSByZ2IoMSwwLDAsMC41KSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIkNvdW50cyBEaXN0cmlidXRpb24gQ29tcGFyaXNvbiAoNjAgU3ViLWludGVydmFscykiLCB4bGFiID0gIk51bWJlciBvZiBQYWxpbmRyb21lcyBTaXRlcyBJbnNpZGUgYW4gSW50ZXJ2YWwiLCB5bGltID0gYygwLDAuNCkpCmxpbmVzKGRlbnNpdHkoY291bnRzLCBhZGp1c3QgPSAyKSwgY29sID0gcmdiKDEsMCwwLDAuNSkpClBvaXMgPC0gcnBvaXMobiwgbGFtYmRhKQpoaXN0KFBvaXMsIGJyZWFrcyA9IDE1LCBjb2wgPSByZ2IoMCwwLDEsMC41KSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBhZGQgPSBUUlVFKQpsaW5lcyhkZW5zaXR5KFBvaXMsIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMCwwLDEsMC41KSkKbGVnZW5kKHggPSAxOCwgeSA9IDAuMTcsIGxlZ2VuZCA9IGMoIlNhbXBsZSIsICJQb2lzc29uIiksIGx0eSA9IGMoMSwxKSwgY29sID0gYyhyZ2IoMSwwLDAsMC41KSwgcmdiKDAsMCwxLDAuNSkpKQoKIyBDaGktc3FyIEdvb2RuZXNzIG9mIEZpdCB0ZXN0CmV4cGVjdGVkIDwtIG4ucmVnaW9uKmV4cCgtbGFtYmRhKSogbGFtYmRhKiooaW50ZXJ2YWwpL2ZhY3RvcmlhbChpbnRlcnZhbCkKZXhwZWN0IDwtIG4ucmVnaW9uKmV4cCgtbGFtYmRhKSogbGFtYmRhKiooMCkvZmFjdG9yaWFsKDApCmV4cGVjdGVkWzFdIDwtIGV4cGVjdGVkWzFdKyBleHBlY3QKZm9yIChpIGluIGMoMTA6MTEpKXsKICBleHBlY3QgPC0gbi5yZWdpb24qZXhwKC1sYW1iZGEpKiBsYW1iZGEqKihpKS9mYWN0b3JpYWwoaSkKICBleHBlY3RlZFs5XSA8LSBleHBlY3RlZFs5XSsgZXhwZWN0Cn0KZXhwZWN0ZWRbMTFdIDwtIDAKZm9yIChpIGluIGMoMToxMikpewogIGV4cGVjdCA8LSBleHAoLWxhbWJkYSkqIGxhbWJkYSoqKGkpL2ZhY3RvcmlhbChpKQogIGV4cGVjdGVkWzExXSA8LSBleHBlY3RlZFsxMV0rIGV4cGVjdAp9CmV4cGVjdGVkWzExXSA8LSAoMS1leHBlY3RlZFsxMV0pKm4ucmVnaW9uCmNvdW50cy5leHBlY3RlZCA8LSBjKCkKY291bnRzLmludGVydmFsIDwtIGMoKQpjb3VudHMub2JzZXJ2ZWQgPC0gYygpCgojIEdyb3VwIGJpbnMKY291bnRzLmV4cGVjdGVkWzFdIDwtIHN1bShleHBlY3RlZFsxOjJdKQpjb3VudHMuZXhwZWN0ZWRbMjo2XSA8LSBleHBlY3RlZFszOjddCmNvdW50cy5leHBlY3RlZFs3XSA8LSBzdW0oZXhwZWN0ZWRbODoxMV0pCmNvdW50cy5vYnNlcnZlZFsxXSA8LSBzdW0ob2JzZXJ2ZWRbMToyXSkKY291bnRzLm9ic2VydmVkWzI6Nl0gPC0gb2JzZXJ2ZWRbMzo3XQpjb3VudHMub2JzZXJ2ZWRbN10gPC0gc3VtKG9ic2VydmVkWzg6MTFdKQpjb3VudHMuaW50ZXJ2YWxbMV0gPC0gaW50ZXJ2YWxbMl0KY291bnRzLmludGVydmFsWzI6Nl0gPC0gaW50ZXJ2YWxbMzo3XQpjb3VudHMuaW50ZXJ2YWxbN10gPC0gaW50ZXJ2YWxbN10rMQpjb3VudHMudGFibGU2MCA8LSBkYXRhLmZyYW1lKGNvdW50cy5pbnRlcnZhbCxjb3VudHMub2JzZXJ2ZWQsY291bnRzLmV4cGVjdGVkKQpjb3VudHMudGFibGU2MAojIENoaS1zcXVhcmUgc3RhdGlzdGljCmNoaV8yIDwtIHN1bSgoY291bnRzLm9ic2VydmVkIC0gY291bnRzLmV4cGVjdGVkKV4yL2NvdW50cy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSA1KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGYgPSA1LCBsb3dlci50YWlsID0gRkFMU0UpCnByaW50KHBhc3RlKCJUaGUgcF92YWx1ZSB3aGVuIHRoZSBkaXN0YW5jZSBpcyBzcGxpdGVkIGludG8gNjAgc3ViLWludGVydmFscyBpcyIsIHBfdmFsdWUpKQojIyBWaXN1YWxpemF0aW9uIG9mIHRoZSBSZXNpZHVhbApSZXNpZHVhbHMgPC0gKGNvdW50cy5vYnNlcnZlZCAtIGNvdW50cy5leHBlY3RlZCkgLyBzcXJ0KGNvdW50cy5leHBlY3RlZCkKcGxvdChSZXNpZHVhbHMsIHR5cGUgPSAnaCcsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUGFsaW5kcm9tZSBjb3VudHMiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBDb3VudHMgKGRpdmlkZWQgaW4gNjAgc3ViLWludGVydmFscykiKQpgYGAKCmBgYHtyfQojIENhc2UgMzogZGl2aWRlZCBieSA4MCBpbnRlcnZhbHMKbi5yZWdpb24gPC0gODAKZ2VuZSA8LSBzZXEoMSxOKQoKb2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyhyZWdpb25zcGxpdChuLnJlZ2lvbiwgZ2VuZSwgbG9jYXRpb25zKSkKaW50ZXJ2YWwgPC0gYXMubnVtZXJpYyhuYW1lcyhyZWdpb25zcGxpdChuLnJlZ2lvbiwgZ2VuZSwgbG9jYXRpb25zKSkpCmxhbWJkYSA8LSBuL24ucmVnaW9uCgojIEhpc3RvZ3JhbSBvZiBjb3VudHMgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgcG9pc3NvbiBkaXN0cmlidXRpb24KY291bnRzIDwtIGFzLnZlY3Rvcih0YWJsZShjdXQobG9jYXRpb25zLCBicmVha3MgPSBzZXEoMCwgTiwgbGVuZ3RoLm91dCA9IG4ucmVnaW9uKzEpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSkKaGlzdChjb3VudHMsIGJyZWFrcyA9IGJpbnMsIGNvbCA9IHJnYigxLDAsMCwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiQ291bnRzIERpc3RyaWJ1dGlvbiBDb21wYXJpc29uICg4MCBTdWItaW50ZXJ2YWxzKSIsIHhsYWIgPSAiTnVtYmVyIG9mIFBhbGluZHJvbWVzIFNpdGVzIEluc2lkZSBhbiBJbnRlcnZhbCIsIHlsaW0gPSBjKDAsMC41KSkKbGluZXMoZGVuc2l0eShjb3VudHMsIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMSwwLDAsMC41KSkKUG9pcyA8LSBycG9pcyhuLCBsYW1iZGEpCmhpc3QoUG9pcywgYnJlYWtzID0gMTUsIGNvbCA9IHJnYigwLDAsMSwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGFkZCA9IFRSVUUpCmxpbmVzKGRlbnNpdHkoUG9pcywgYWRqdXN0ID0gMiksIGNvbCA9IHJnYigwLDAsMSwwLjUpKQpsZWdlbmQoeCA9IDE4LCB5ID0gMC4xNywgbGVnZW5kID0gYygiU2FtcGxlIiwgIlBvaXNzb24iKSwgbHR5ID0gYygxLDEpLCBjb2wgPSBjKHJnYigxLDAsMCwwLjUpLCByZ2IoMCwwLDEsMC41KSkpCgojIENoaS1zcXIgR29vZG5lc3Mgb2YgRml0IHRlc3QKZXhwZWN0ZWQgPC0gbi5yZWdpb24qZXhwKC1sYW1iZGEpKiBsYW1iZGEqKihpbnRlcnZhbCkvZmFjdG9yaWFsKGludGVydmFsKQpmb3IgKGkgaW4gYyg4OjkpKXsKICBleHBlY3QgPC0gbi5yZWdpb24qZXhwKC1sYW1iZGEpKiBsYW1iZGEqKihpKS9mYWN0b3JpYWwoaSkKICBleHBlY3RlZFs5XSA8LSBleHBlY3RlZFs5XSsgZXhwZWN0Cn0KZXhwZWN0ZWRbMTBdIDwtIDAKZm9yIChpIGluIGMoMToxMCkpewogIGV4cGVjdCA8LSBleHAoLWxhbWJkYSkqIGxhbWJkYSoqKGkpL2ZhY3RvcmlhbChpKQogIGV4cGVjdGVkWzEwXSA8LSBleHBlY3RlZFsxMF0rIGV4cGVjdAp9CmV4cGVjdGVkWzEwXSA8LSAoMS1leHBlY3RlZFsxMF0pKm4ucmVnaW9uCmNvdW50cy5leHBlY3RlZCA8LSBjKCkKY291bnRzLmludGVydmFsIDwtIGMoKQpjb3VudHMub2JzZXJ2ZWQgPC0gYygpCiMgR3JvdXAgYmlucwpjb3VudHMuZXhwZWN0ZWRbMV0gPC0gc3VtKGV4cGVjdGVkWzE6Ml0pCmNvdW50cy5leHBlY3RlZFsyOjZdIDwtIGV4cGVjdGVkWzM6N10KY291bnRzLmV4cGVjdGVkWzddIDwtIHN1bShleHBlY3RlZFs4OjEwXSkKY291bnRzLm9ic2VydmVkWzFdIDwtIHN1bShvYnNlcnZlZFsxOjJdKQpjb3VudHMub2JzZXJ2ZWRbMjo2XSA8LSBvYnNlcnZlZFszOjddCmNvdW50cy5vYnNlcnZlZFs3XSA8LSBzdW0ob2JzZXJ2ZWRbODoxMF0pCmNvdW50cy5pbnRlcnZhbFsxXSA8LSBpbnRlcnZhbFsyXQpjb3VudHMuaW50ZXJ2YWxbMjo2XSA8LSBpbnRlcnZhbFszOjddCmNvdW50cy5pbnRlcnZhbFs3XSA8LSBpbnRlcnZhbFs3XSsxCmNvdW50cy50YWJsZTYwIDwtIGRhdGEuZnJhbWUoY291bnRzLmludGVydmFsLGNvdW50cy5vYnNlcnZlZCxjb3VudHMuZXhwZWN0ZWQpCmNvdW50cy50YWJsZTYwCiMgQ2hpLXNxdWFyZSBzdGF0aXN0aWMKY2hpXzIgPC0gc3VtKChjb3VudHMub2JzZXJ2ZWQgLSBjb3VudHMuZXhwZWN0ZWQpXjIvY291bnRzLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHAgPSAwLjk1LCBkZiA9IDUpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZiA9IDUsIGxvd2VyLnRhaWwgPSBGQUxTRSkKcHJpbnQocGFzdGUoIlRoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byA2MCBzdWItaW50ZXJ2YWxzIGlzIiwgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoY291bnRzLm9ic2VydmVkIC0gY291bnRzLmV4cGVjdGVkKSAvIHNxcnQoY291bnRzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZSA9ICdoJywgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJQYWxpbmRyb21lIGNvdW50cyIsIG1haW4gPSAiUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIENvdW50cyAoZGl2aWRlZCBpbiA4MCBzdWItaW50ZXJ2YWxzKSIpCmBgYAoKCiMjIFNjZW5hcmlvIDQ6IFRoZSBCaWdnZXN0IENsdXN0ZXIKRG9lcyB0aGUgaW50ZXJ2YWwgd2l0aCB0aGUgZ3JlYXRlc3QgbnVtYmVyIG9mIHBhbGluZHJvbWVzIGluZGljYXRlIGEgcG90ZW50aWFsIG9yaWdpbiBvZiByZXBsaWNhdGlvbj8gQmUgY2FyZWZ1bCBpbiBtYWtpbmcgeW91ciBpbnRlcnZhbHMsIGZvciBhbnkgc21hbGwsIGJ1dCBzaWduaWZpY2FudCBkZXZpYXRpb25zIGZyb20gcmFuZG9tIHNjYXR0ZXIsIHN1Y2ggYXMgYSB0aWdodCBjbHVzdGVyIG9mIGEgZmV3IHBhbGluZHJvbWVzLCBjb3VsZCBlYXNpbHkgZ28gdW5kZXRlY3RlZCBpZiB0aGUgcmVnaW9ucyBleGFtaW5lZCBhcmUgdG9vIGxhcmdlLiBBbHNvLCBpZiB0aGUgcmVnaW9ucyBhcmUgdG9vIHNtYWxsLCBhIGNsdXN0ZXIgb2YgcGFsaW5kcm9tZXMgbWF5IGJlIHNwbGl0IGJldHdlZW4gYWRqYWNlbnQgaW50ZXJhdmxzIGFuZCBub3QgYXBwZWFyIGFzIGEgaGlnaC1jb3VudCBpbnRlcnZhbC4KYGBge3J9CmZpbmFsIDwtIGFycmF5KGRpbT1jKDUwMCwxKSkKaW50ZXJ2YWxfbGVuZ3RoIDwtIGFycmF5KGRpbT1jKDUwMCwxKSkKbGFtZGEgPC0gYXJyYXkoZGltPWMoNTAwLDEpKQpmb3IgKGsgaW4gMjA6MTAwKXsKICB0YWIgPC0gdGFibGUoY3V0KGxvY2F0aW9ucywgYnJlYWtzPXNlcSgwLCBOLCBsZW5ndGgub3V0PWsrMSksIGluY2x1ZGUubG93ZXN0PVRSVUUpKQogIGhlYWQodGFiLDEwKQogIHRhYjwtYXMudmVjdG9yKHRhYikKICBsYW1kYVtrLF0gPC1zdW0odGFiKS9rCiAgdGhyZXNob2xkIDwtbWF4KHRhYikKICByZXN1bHQgPC0gMAogIGludGVydmFsX2xlbmd0aFtrLF0gPC0gTi9rCiAgZm9yIChpIGluIDA6KHRocmVzaG9sZC0xKSl7CiAgICByZXN1bHQgPC0gcmVzdWx0KygobGFtZGFba11eaSkqZXhwKC1sYW1kYVtrXSkvZmFjdG9yaWFsKGkpKQogIH0KICBmaW5hbFtrLF0gPC0gMS1yZXN1bHReawp9CnJlc3VsdCA8LSBkYXRhLmZyYW1lKGxhbWRhLGludGVydmFsX2xlbmd0aCxmaW5hbCkKCiMgRGlzcGxheSBUYWJsZSBjb250YWluaW5nIHRoZSBwcm9iYWJpbGl0eSBvZiBhIFBvaXNzb24gRGlzdHJpYnV0aW9uIGhhdmluZyBlIGdyZWF0ZXN0IG51bWJlciBvZiBoaXRzIGF0IGxlYXN0IGsgZm9yIGVhY2ggc3ViLWludGVydmFsIGRpdmlzaW9ucwpyZXN1bHRbYyg0MCw2MCw4MCksXQpgYGAKCgojIyBBZGRpdGlvbmFsIFNjZW5hcmlvOiBISVYgYW5kIEFnZQpUT0RPIERlc2NyaXB0aW9uCmBgYHtyfQojIENsZWFuIG91dCAndW5rbm93bicgZGF0YSBhbmQgY29udmVydCBmYWN0b3IgdG8gbnVtZXJpY2FsIHZhbHVlcwpoZWFsdGggPC0gdHJhbnNmb3JtKGhlYWx0aCwgYWdlX3lycz1hcy5udW1lcmljKGFnZV95cnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGl2PWFzLmNoYXJhY3RlcihoaXYpKQpoZWFsdGguaW5kIDwtIHdoaWNoKGhlYWx0aCRoaXYgIT0gJ3Vua25vd24nKQpoZWFsdGggPC0gaGVhbHRoW2hlYWx0aC5pbmQsXQoKIyBUb3RhbCBudW1iZXIgb2YgcGVvcGxlIHRoYXQgaGF2ZSBoaXYKcG9wdWxhdGlvbj1ucm93KGhlYWx0aCkKcG9wX2hpdiA8LSBucm93KGhlYWx0aFt3aGljaChoZWFsdGgkaGl2PT0ncG9zaXRpdmUnKSxdKQoKIyBTcGxpdCB0aGUgYWdlIGludG8gZm91ciBncm91cHMKIyAwLTIwCmFnZV9maXJzdCA8LSBoZWFsdGgkYWdlX3lyc1t3aGljaChoZWFsdGgkYWdlX3lyczwyMSldCmFnZV9wcm9wb3J0aW9uX2ZpcnN0IDwtIGxlbmd0aChhZ2VfZmlyc3QpL3BvcHVsYXRpb24KaGl2X3Byb3BvcnRpb25fZmlyc3Q8LSBucm93KGhlYWx0aFt3aGljaCgoaGVhbHRoJGhpdj09ICdwb3NpdGl2ZScpICYgKGhlYWx0aCRhZ2VfeXJzIDwyMSkpLF0pL3BvcF9oaXYKCiMgMjEtNDAKYWdlX3NlY29uZDwtaGVhbHRoJGFnZV95cnNbd2hpY2goaGVhbHRoJGFnZV95cnM+MjAgJiBoZWFsdGhbJ2FnZV95cnMnXTw0MSldCmFnZV9wcm9wb3J0aW9uX3NlY29uZCA8LSBsZW5ndGgoYWdlX3NlY29uZCkvcG9wdWxhdGlvbgpoaXZfcHJvcG9ydGlvbl9zZWNvbmQ8LW5yb3coaGVhbHRoW3doaWNoKGhlYWx0aCRhZ2VfeXJzPjIwICZoZWFsdGgkYWdlX3lyczw0MSAmIGhlYWx0aCRoaXY9PSdwb3NpdGl2ZScpLF0pL3BvcF9oaXYKCiMgNDEtNjAKYWdlX3RoaXJkPC1oZWFsdGgkYWdlX3lyc1t3aGljaChoZWFsdGhbJ2FnZV95cnMnXT40MCAmIGhlYWx0aFsnYWdlX3lycyddPDYxKV0KYWdlX3Byb3BvcnRpb25fdGhpcmQgPC0gbGVuZ3RoKGFnZV90aGlyZCkvcG9wdWxhdGlvbgpoaXZfcHJvcG9ydGlvbl90aGlyZDwtbnJvdyhoZWFsdGhbd2hpY2goaGVhbHRoJGFnZV95cnM+NDAgJiBoZWFsdGgkYWdlX3lyczw2MSAmaGVhbHRoJGhpdj09J3Bvc2l0aXZlJyksXSkvcG9wX2hpdgoKIyA2MSsKYWdlX2xhc3Q8LWhlYWx0aCRhZ2VfeXJzW3doaWNoKGhlYWx0aFsnYWdlX3lycyddPjYwKV0KYWdlX3Byb3BvcnRpb25fbGFzdCA8LSBsZW5ndGgoYWdlX2xhc3QpL3BvcHVsYXRpb24KaGl2X3Byb3BvcnRpb25fbGFzdDwtbnJvdyhoZWFsdGhbd2hpY2goaGVhbHRoJGFnZV95cnM+NjAgJiBoZWFsdGgkaGl2PT0ncG9zaXRpdmUnKSxdKS9wb3BfaGl2CgojIEV4cGVjdGVkIERhdGEKcG9wdWxhdGlvbl9kaXN0IDwtYyhhZ2VfcHJvcG9ydGlvbl9maXJzdCxhZ2VfcHJvcG9ydGlvbl9zZWNvbmQsYWdlX3Byb3BvcnRpb25fdGhpcmQsYWdlX3Byb3BvcnRpb25fbGFzdCkKIyBPYnNlcnZlZCBEYXRhCmhpdl9kaXN0PC1jKGhpdl9wcm9wb3J0aW9uX2ZpcnN0LGhpdl9wcm9wb3J0aW9uX3NlY29uZCxoaXZfcHJvcG9ydGlvbl90aGlyZCxoaXZfcHJvcG9ydGlvbl9sYXN0KQphZ2VfZGlzdCA8LSBjKCIwLTIwIiwgIjIxLTQwIiwgIjQxLTYwIiwgIjYxKyIpCmRhdGEuZnJhbWUoYWdlX2Rpc3QscG9wdWxhdGlvbl9kaXN0LGhpdl9kaXN0KQoKIyBHb29kbmVzcy1maXR0ZXN0CmNoaV8yIDwtIHN1bSgoaGl2X2Rpc3QgLSBwb3B1bGF0aW9uX2Rpc3QpXjIvcG9wdWxhdGlvbl9kaXN0KQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHA9MC45NSwgZGY9MykKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmPTMsIGxvd2VyLnRhaWw9RkFMU0UpCnByaW50KHBhc3RlKCdUaGUgcF92YWx1ZSBvZiBHb29kbmVzcyBvZiBGaXQgVGVzdCBpcycscF92YWx1ZSkpCgojVmlzdWFsaXphdGlvbgpSZXNpZHVhbHMgPC0gKGhpdl9kaXN0IC0gcG9wdWxhdGlvbl9kaXN0KSAvIHNxcnQocG9wdWxhdGlvbl9kaXN0KQpwbG90KFJlc2lkdWFscywgdHlwZT0naCcsIHlsYWI9J1N0YW5kYXJkaXplZCBSZXNpZHVhbHMnLCB4bGFiPSdQcm9wb3J0aW9uIG9mIFBvc2l0aXZlIEhJVicsIG1haW49J1Bsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBBZ2UgYW5kIEhJViBQb3NpdGl2ZScpCmBgYApOdWxsIEh5cG90aGVzaXM6IFRoZSBwcm9wb3J0aW9uIG9mIGFnZSBpbiB0aGUgcG9wdWxhdGlvbiBpcyB1bnJlbGF0ZWQgd2l0aCB0aGUgcHJvcG9ydGlvbiBvZiBwZW9wbGUgaGF2aW5nIGhpdi4oQWdlIGlzIG5vdCBhbiBpbmZsdWVuY2luZyBmYWN0b3IgZm9yIEhJViB0ZXN0aW5nIHBvc2l0aXZlKQpTaW5jZSBwLXZhbHVlIG9mIHRoaXMgY2hpLXNxdWFyZSBnb29kbmVzcyBvZiBmaXQgdGVzdCBpcyBjbG9zZSB0byAxLCB3ZSBzZWUgdGhhdCBkZXZpYXRpb25zIGFzIGxhcmdlIGFzIG91cnMgKG9yIGxhcmdlcikgYXJlIHZlcnkgbGlrZWx5LiBJbiBhZGRpdGlvbiwgaGF2aW5nIHZhbHVlcyBvZiB0aGUgc3RhbmRhcmRpemVkIHJlc2lkdWFsIGxlc3MgdGhhbiAzIHN1Z2dlc3RzIHRoYXQgaXQgaXMgYSBnb29kIGZpdCBvZiB0aGUgYWdlIGRpc3RyaWJ1dGlvbiB0byBlc3RpbWF0ZSB0aGUgcGVvcGxlIHRlc3RpbmcgcG9zaXRpdmUgb24gaGl2LiBIZW5jZSwgd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYW5kIGNvbmNsdWRlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcm9wb3J0aW9uIG9mIGFnZSBtYXRjaGVzIHdpdGggdGhlIHRoZSBkaXN0cmlidXRpb24gb2YgcGVvcGxlIHRlc3RpbmcgcG9zaXRpdmUgb24gSElWLg==